Add routes
This commit is contained in:
		
							
								
								
									
										521
									
								
								routes/mj.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								routes/mj.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,521 @@
 | 
			
		||||
let dict = require('./translate');
 | 
			
		||||
 | 
			
		||||
function Tile(t) {
 | 
			
		||||
	let m = Math.floor(t / 36),
 | 
			
		||||
		d = t % 36,
 | 
			
		||||
		n = Math.floor(d / 4) + 1,
 | 
			
		||||
		res;
 | 
			
		||||
	switch (m) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			res = 'm' + (d == 16 ? 'a' : n);
 | 
			
		||||
			break;
 | 
			
		||||
		case 1:
 | 
			
		||||
			res = 'p' + (d == 16 ? 'a' : n);
 | 
			
		||||
			break;
 | 
			
		||||
		case 2:
 | 
			
		||||
			res = 's' + (d == 16 ? 'a' : n);
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			let s = Math.floor(d / 4);
 | 
			
		||||
			switch (s) {
 | 
			
		||||
				case 0:
 | 
			
		||||
					res = 'tan';
 | 
			
		||||
					break;
 | 
			
		||||
				case 1:
 | 
			
		||||
					res = 'nan';
 | 
			
		||||
					break;
 | 
			
		||||
				case 2:
 | 
			
		||||
					res = 'xia';
 | 
			
		||||
					break;
 | 
			
		||||
				case 3:
 | 
			
		||||
					res = 'pei';
 | 
			
		||||
					break;
 | 
			
		||||
				case 4:
 | 
			
		||||
					res = 'haku';
 | 
			
		||||
					break;
 | 
			
		||||
				case 5:
 | 
			
		||||
					res = 'hatsu';
 | 
			
		||||
					break;
 | 
			
		||||
				case 6:
 | 
			
		||||
					res = 'chun';
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Hand(h) {
 | 
			
		||||
	return h.map(function(t) {
 | 
			
		||||
		return Tile(t);
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function replaceTiles(trg) {
 | 
			
		||||
	let res = trg;
 | 
			
		||||
	for (var r = 0; r < res.rounds.length; r++) {
 | 
			
		||||
		// обработка конечных рук
 | 
			
		||||
		for (var f = 0; f < res.rounds[r].finish.length; f++) {
 | 
			
		||||
			let finish = res.rounds[r].finish[f].sort(function(a, b) {
 | 
			
		||||
				return a - b;
 | 
			
		||||
			});
 | 
			
		||||
			res.rounds[r].finish[f] = Hand(res.rounds[r].finish[f]).slice();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function decodeList(list, to_int, sort) {
 | 
			
		||||
	if (typeof sort === 'undefined') sort = false;
 | 
			
		||||
	let res = list.split(',');
 | 
			
		||||
	res = (to_int ? res.map(function(a) {
 | 
			
		||||
		return parseInt(a);
 | 
			
		||||
	}) : res);
 | 
			
		||||
	return (sort ? res.sort(function(a, b) {
 | 
			
		||||
		return a - b;
 | 
			
		||||
	}) : res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parseScore(data, mult) {
 | 
			
		||||
	let res = [];
 | 
			
		||||
	for (var i = 0; i < data.length / 2; i++) {
 | 
			
		||||
		res.push({
 | 
			
		||||
			begin: data[2 * i] * mult,
 | 
			
		||||
			diff: data[2 * i + 1] * mult,
 | 
			
		||||
			end: (data[2 * i] + data[2 * i + 1]) * mult
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parseMeld(data) {
 | 
			
		||||
	if (data & 0x4) {
 | 
			
		||||
		return parse_chi(data);
 | 
			
		||||
	} else if (data & 0x18) {
 | 
			
		||||
		return parse_pon(data);
 | 
			
		||||
	} else if (data & 0x20) {
 | 
			
		||||
		return parse_nuki(data);
 | 
			
		||||
	} else {
 | 
			
		||||
		return parse_kan(data);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parse_chi(data) {
 | 
			
		||||
	let t0 = (data >> 3) & 0x3,
 | 
			
		||||
		t1 = (data >> 5) & 0x3,
 | 
			
		||||
		t2 = (data >> 7) & 0x3,
 | 
			
		||||
		base_and_called = data >> 10,
 | 
			
		||||
		base = Math.floor(base_and_called / 3),
 | 
			
		||||
		called = base_and_called - 3 * base;
 | 
			
		||||
	base = Math.floor(base / 7) * 9 + base % 7;
 | 
			
		||||
	return {
 | 
			
		||||
		type: 'chi',
 | 
			
		||||
		fromPlayer: (data & 0x3),
 | 
			
		||||
		called: called,
 | 
			
		||||
		tiles: [t0 + 4 * (base + 0), t1 + 4 * (base + 1), t2 + 4 * (base + 2)]
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parse_pon(data) {
 | 
			
		||||
	let t4 = (data >> 5) & 0x3,
 | 
			
		||||
		arr = [
 | 
			
		||||
			[1, 2, 3],
 | 
			
		||||
			[0, 2, 3],
 | 
			
		||||
			[0, 1, 3],
 | 
			
		||||
			[0, 1, 2]
 | 
			
		||||
		],
 | 
			
		||||
		t0 = arr[t4][0],
 | 
			
		||||
		t1 = arr[t4][1],
 | 
			
		||||
		t2 = arr[t4][2],
 | 
			
		||||
		base_and_called = data >> 9,
 | 
			
		||||
		base = Math.floor(base_and_called / 3),
 | 
			
		||||
		called = base_and_called - 3 * base;
 | 
			
		||||
	if (data & 0x8) {
 | 
			
		||||
		return {
 | 
			
		||||
			type: 'pon',
 | 
			
		||||
			fromPlayer: (data & 0x3),
 | 
			
		||||
			called: called,
 | 
			
		||||
			tiles: [t0 + 4 * base, t1 + 4 * base, t2 + 4 * base]
 | 
			
		||||
		};
 | 
			
		||||
	} else {
 | 
			
		||||
		return {
 | 
			
		||||
			type: 'chakan',
 | 
			
		||||
			fromPlayer: (data & 0x3),
 | 
			
		||||
			called: called,
 | 
			
		||||
			tiles: [t0 + 4 * base, t1 + 4 * base, t2 + 4 * base, t4 + 4 * base]
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parse_kan(data) {
 | 
			
		||||
	let base_and_called = data >> 8,
 | 
			
		||||
		base = Math.floor(base_and_called / 4),
 | 
			
		||||
		called = base_and_called - 4 * base;
 | 
			
		||||
	return {
 | 
			
		||||
		type: 'kan',
 | 
			
		||||
		fromPlayer: (data & 0x3),
 | 
			
		||||
		called: called,
 | 
			
		||||
		tiles: [4 * base, 1 + 4 * base, 2 + 4 * base, 3 + 4 * base]
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parse_nuki(data) {
 | 
			
		||||
	return {
 | 
			
		||||
		type: 'nuki',
 | 
			
		||||
		tiles: [data >> 8]
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parseLog(namelog, file, lang) {
 | 
			
		||||
	return new Promise(function(resolve, reject) {
 | 
			
		||||
		var fs = require('fs'),
 | 
			
		||||
			xmlparser = require('htmlparser2');
 | 
			
		||||
		var parser = new xmlparser.Parser({
 | 
			
		||||
			onopentag: function(name, attribs) {
 | 
			
		||||
				switch (true) {
 | 
			
		||||
					case /mjloggm/.test(name):
 | 
			
		||||
						res = {
 | 
			
		||||
							log: namelog,
 | 
			
		||||
							players: [],
 | 
			
		||||
							rounds: []
 | 
			
		||||
						};
 | 
			
		||||
						break;
 | 
			
		||||
					case /un/.test(name) && (typeof attribs.sx !== 'undefined'):
 | 
			
		||||
						var sex = decodeList(attribs.sx),
 | 
			
		||||
							dan = decodeList(attribs.dan),
 | 
			
		||||
							rate = decodeList(attribs.rate);
 | 
			
		||||
						[0, 1, 2, 3].forEach(function(p) {
 | 
			
		||||
							res.players[p] = {
 | 
			
		||||
								name: decodeURIComponent(attribs['n' + p]),
 | 
			
		||||
								sex: sex[p],
 | 
			
		||||
								dan: parseInt(dan[p]),
 | 
			
		||||
								rate: parseFloat(rate[p]),
 | 
			
		||||
								connected: true
 | 
			
		||||
							};
 | 
			
		||||
						});
 | 
			
		||||
						break;
 | 
			
		||||
					case /bye/.test(name) && (typeof attribs.who !== 'undefined'):
 | 
			
		||||
						res.players[parseInt(attribs.who)].connected = false;
 | 
			
		||||
						break;
 | 
			
		||||
					case /taikyoku/.test(name) && (typeof attribs.oya !== 'undefined'):
 | 
			
		||||
						res.diler = parseInt(attribs.oya);
 | 
			
		||||
						break;
 | 
			
		||||
					case /init/.test(name) && (typeof attribs.oya !== 'undefined'):
 | 
			
		||||
						let seed = decodeList(attribs.seed);
 | 
			
		||||
						res.rounds.push({
 | 
			
		||||
							name: parseInt(seed[0]),
 | 
			
		||||
							honba: parseInt(seed[1]),
 | 
			
		||||
							riichi: {
 | 
			
		||||
								count: parseInt(seed[2]),
 | 
			
		||||
								player: []
 | 
			
		||||
							},
 | 
			
		||||
							d0: parseInt(seed[3]),
 | 
			
		||||
							d1: parseInt(seed[4]),
 | 
			
		||||
							dora: Hand([parseInt(seed[5])]),
 | 
			
		||||
							uradora: [],
 | 
			
		||||
							diler: parseInt(attribs.oya),
 | 
			
		||||
							hands: [],
 | 
			
		||||
							finish: [],
 | 
			
		||||
							meld: [
 | 
			
		||||
								[],
 | 
			
		||||
								[],
 | 
			
		||||
								[],
 | 
			
		||||
								[]
 | 
			
		||||
							],
 | 
			
		||||
							events: [
 | 
			
		||||
								[],
 | 
			
		||||
								[],
 | 
			
		||||
								[],
 | 
			
		||||
								[]
 | 
			
		||||
							]
 | 
			
		||||
						});
 | 
			
		||||
						cur_game = res.rounds.length - 1;
 | 
			
		||||
						cur_player = res.rounds[cur_game].diler;
 | 
			
		||||
						cur_event = -1;
 | 
			
		||||
						[0, 1, 2, 3].forEach(function(p) {
 | 
			
		||||
							res.rounds[cur_game].finish[p] = decodeList(attribs['hai' + p], true, true);
 | 
			
		||||
							res.rounds[cur_game].hands[p] = Hand(decodeList(attribs['hai' + p], true, true));
 | 
			
		||||
						});
 | 
			
		||||
						break;
 | 
			
		||||
					case /agari/.test(name) && (typeof attribs.ba !== 'undefined'):
 | 
			
		||||
						if (!res.rounds[cur_game].win) res.rounds[cur_game].win = [];
 | 
			
		||||
						let ten = decodeList(attribs.ten),
 | 
			
		||||
						    hand = decodeList(attribs.hai, true, true),
 | 
			
		||||
						    machi = decodeList(attribs.machi, true, false),
 | 
			
		||||
						    wintile = hand.indexOf(machi[0]),
 | 
			
		||||
							agari = {
 | 
			
		||||
								type: (attribs.fromwho != attribs.who ? 'ron' : 'tsumo'),
 | 
			
		||||
								player: parseInt(attribs.who),
 | 
			
		||||
								hand: Hand(hand),
 | 
			
		||||
								points: parseInt(ten[1]),
 | 
			
		||||
								fu: parseInt(ten[0]),
 | 
			
		||||
								dora: Hand(decodeList(attribs.dorahai, true, false)),
 | 
			
		||||
								machi: wintile
 | 
			
		||||
							};
 | 
			
		||||
						if (attribs.m) {
 | 
			
		||||
							agari.melds = [];
 | 
			
		||||
							decodeList(attribs.m, true, false).forEach(function(m) {
 | 
			
		||||
								agari.melds.push(parseMeld(m));
 | 
			
		||||
							});
 | 
			
		||||
							agari.closed = [];
 | 
			
		||||
							agari.melds.forEach(function(m) {
 | 
			
		||||
								m.tiles = Hand(m.tiles);
 | 
			
		||||
								if (!m.fromPlayer) {
 | 
			
		||||
									agari.closed.push(m);
 | 
			
		||||
								}
 | 
			
		||||
							});
 | 
			
		||||
						} else {
 | 
			
		||||
							agari.closed = true;
 | 
			
		||||
						}
 | 
			
		||||
						if (parseInt(ten[2]) > 0) {
 | 
			
		||||
							agari.limit = parseInt(ten[2]);
 | 
			
		||||
						}
 | 
			
		||||
						if (attribs.dorahaiura) {
 | 
			
		||||
							agari.uradora = Hand(decodeList(attribs.dorahaiura, true, false));
 | 
			
		||||
							res.rounds[cur_game].uradora = agari.uradora;
 | 
			
		||||
						}
 | 
			
		||||
						if (agari.type == 'ron') {
 | 
			
		||||
							agari.fromPlayer = parseInt(attribs.fromwho);
 | 
			
		||||
							res.rounds[cur_game].events[agari.fromPlayer][res.rounds[cur_game].events[agari.fromPlayer].length - 1].furikomi = 1;
 | 
			
		||||
						}
 | 
			
		||||
						if (attribs.yaku) {
 | 
			
		||||
							let yakus = decodeList(attribs.yaku);
 | 
			
		||||
							agari.yakulist = [];
 | 
			
		||||
							for (var y = 0; y < yakus.length / 2; y++) {
 | 
			
		||||
								agari.yakulist.push({
 | 
			
		||||
									yaku: parseInt(yakus[2 * y]),
 | 
			
		||||
									han: parseInt(yakus[2 * y + 1])
 | 
			
		||||
								});
 | 
			
		||||
							}
 | 
			
		||||
						} else if (attribs.yakuman) {
 | 
			
		||||
							agari.yakuman = decodeList(attribs.yakuman, true, true);
 | 
			
		||||
						}
 | 
			
		||||
						if (attribs.sc) {
 | 
			
		||||
							res.rounds[cur_game].score = parseScore(decodeList(attribs.sc, true, false), 100);
 | 
			
		||||
						}
 | 
			
		||||
						res.rounds[cur_game].win.push(agari);
 | 
			
		||||
						res.rounds[cur_game].finish[parseInt(attribs.who)] = [];
 | 
			
		||||
						break;
 | 
			
		||||
					case /ryuukyoku/.test(name) && (typeof attribs.ba !== 'undefined'):
 | 
			
		||||
						res.rounds[cur_game].draw = {};
 | 
			
		||||
						if (attribs.type) {
 | 
			
		||||
							res.rounds[cur_game].draw.type = attribs.type;
 | 
			
		||||
						}
 | 
			
		||||
						if (attribs.sc) {
 | 
			
		||||
							res.rounds[cur_game].score = parseScore(decodeList(attribs.sc, true, false), 100);
 | 
			
		||||
						}
 | 
			
		||||
						if (attribs.ba) {
 | 
			
		||||
							let ba = decodeList(attribs.ba, true, false);
 | 
			
		||||
							res.rounds[cur_game].draw.honba = ba[0];
 | 
			
		||||
							res.rounds[cur_game].draw.riichi = ba[1];
 | 
			
		||||
						}
 | 
			
		||||
						if (res.rounds[cur_game].draw || res.rounds[cur_game].draw.type === 'nm') {
 | 
			
		||||
							res.rounds[cur_game].draw.tenpai = [];
 | 
			
		||||
							[0, 1, 2, 3].forEach(function(p) {
 | 
			
		||||
								let hand = [];
 | 
			
		||||
								if (attribs['hai' + p]) {
 | 
			
		||||
									hand = Hand(decodeList(attribs['hai' + p], true, true));
 | 
			
		||||
									res.rounds[cur_game].finish[p] = [];
 | 
			
		||||
								}
 | 
			
		||||
								res.rounds[cur_game].draw.tenpai.push(hand);
 | 
			
		||||
							});
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
					case /^[t-w]\d+/.test(name):
 | 
			
		||||
						let draw = /^([t-w])(\d+)$/.exec(name);
 | 
			
		||||
						cur_player = ['t', 'u', 'v', 'w'].indexOf(draw[1]);
 | 
			
		||||
						// Проверим тайл замены при кане
 | 
			
		||||
						if (res.rounds[cur_game].events[cur_player][cur_event] &&
 | 
			
		||||
							res.rounds[cur_game].events[cur_player][cur_event].call &&
 | 
			
		||||
							(res.rounds[cur_game].events[cur_player][cur_event].call.type === 'kan' ||
 | 
			
		||||
								res.rounds[cur_game].events[cur_player][cur_event].call.type === 'chakan')
 | 
			
		||||
						) {
 | 
			
		||||
							res.rounds[cur_game].events[cur_player][cur_event].draw = Tile(parseInt(draw[2]));
 | 
			
		||||
						} else {
 | 
			
		||||
							// начало ходов начинаем с дилера
 | 
			
		||||
							if (cur_player === res.rounds[cur_game].diler) cur_event++;
 | 
			
		||||
							res.rounds[cur_game].events[cur_player][cur_event] = {
 | 
			
		||||
								draw: Tile(parseInt(draw[2]))
 | 
			
		||||
							};
 | 
			
		||||
						}
 | 
			
		||||
						res.rounds[cur_game].finish[cur_player].push(parseInt(draw[2]));
 | 
			
		||||
						break;
 | 
			
		||||
					case /^[d-g]\d+/.test(name):
 | 
			
		||||
						let discard = /^([d-g])(\d+)$/.exec(name);
 | 
			
		||||
						cur_player = ['d', 'e', 'f', 'g'].indexOf(discard[1]);
 | 
			
		||||
						// Обработка цумогири
 | 
			
		||||
						if (!res.rounds[cur_game].events[cur_player][cur_event].call &&
 | 
			
		||||
							!res.rounds[cur_game].events[cur_player][cur_event].reach &&
 | 
			
		||||
							res.rounds[cur_game].events[cur_player][cur_event].draw == Tile(parseInt(discard[2]))) {
 | 
			
		||||
							delete res.rounds[cur_game].events[cur_player][cur_event].draw;
 | 
			
		||||
							res.rounds[cur_game].events[cur_player][cur_event].tsumogiri = Tile(parseInt(discard[2]));
 | 
			
		||||
						} else {
 | 
			
		||||
							res.rounds[cur_game].events[cur_player][cur_event].discard = Tile(parseInt(discard[2]));
 | 
			
		||||
						}
 | 
			
		||||
						// Уберем из финальной руки выкинутый тайл
 | 
			
		||||
						if (res.rounds[cur_game].finish[cur_player].indexOf(parseInt(discard[2])) >= 0) {
 | 
			
		||||
							res.rounds[cur_game].finish[cur_player].splice(
 | 
			
		||||
								res.rounds[cur_game].finish[cur_player].indexOf(parseInt(discard[2])), 1);
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
					case /dora/.test(name) && (typeof attribs.hai !== 'undefined'):
 | 
			
		||||
						res.rounds[cur_game].events[cur_player][cur_event].dora = Tile(parseInt(attribs.hai));
 | 
			
		||||
						res.rounds[cur_game].dora.push(Tile(parseInt(attribs.hai)));
 | 
			
		||||
						break;
 | 
			
		||||
					case /n/.test(name) && (typeof attribs.m !== 'undefined'):
 | 
			
		||||
						let meld = parseMeld(attribs.m);
 | 
			
		||||
						meld.player = parseInt(attribs.who);
 | 
			
		||||
						// уберем из финальной руки сет
 | 
			
		||||
						for (var i = 0; i < meld.tiles.length; i++) {
 | 
			
		||||
							if (res.rounds[cur_game].finish[meld.player].indexOf(parseInt(meld.tiles[i])) >= 0) {
 | 
			
		||||
								res.rounds[cur_game].finish[meld.player].splice(
 | 
			
		||||
									res.rounds[cur_game].finish[meld.player].indexOf(parseInt(meld.tiles[i])), 1);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						meld.tiles = Hand(meld.tiles);
 | 
			
		||||
						if (meld.type != 'chakan') {
 | 
			
		||||
							// проверим нужно ли увеличить счетчик хода
 | 
			
		||||
							let tmp_meld = meld.player - res.rounds[cur_game].diler,
 | 
			
		||||
								tmp_player = cur_player - res.rounds[cur_game].diler;
 | 
			
		||||
							tmp_meld = (tmp_meld < 0 ? tmp_meld + 4 : tmp_meld);
 | 
			
		||||
							tmp_player = (tmp_player < 0 ? tmp_player + 4 : tmp_player);
 | 
			
		||||
							if (tmp_meld < tmp_player) {
 | 
			
		||||
								cur_event++;
 | 
			
		||||
							}
 | 
			
		||||
							meld.rotate = (meld.player - cur_player + 4) % 4;
 | 
			
		||||
							if (meld.type === 'pon') {
 | 
			
		||||
								meld.rotate--;
 | 
			
		||||
							} else if (meld.type === 'kan') {
 | 
			
		||||
								meld.rotate = [4, 0, 1, 3][meld.rotate];
 | 
			
		||||
							}
 | 
			
		||||
							res.rounds[cur_game].events[meld.player][cur_event] = {
 | 
			
		||||
								call: meld
 | 
			
		||||
							};
 | 
			
		||||
						} else {
 | 
			
		||||
							res.rounds[cur_game].meld[meld.player].forEach(function(item, i) {
 | 
			
		||||
								if (item.type === 'pon' && item.tiles[0] === meld.tiles[0]) {
 | 
			
		||||
									res.rounds[cur_game].meld[meld.player].splice(i, 1);
 | 
			
		||||
									meld.rotate = item.rotate;
 | 
			
		||||
								}
 | 
			
		||||
							});
 | 
			
		||||
							res.rounds[cur_game].events[meld.player][cur_event].call = meld;
 | 
			
		||||
						}
 | 
			
		||||
						res.rounds[cur_game].meld[meld.player].push(meld);
 | 
			
		||||
						cur_player = meld.player;
 | 
			
		||||
						break;
 | 
			
		||||
					case /reach/.test(name) && (typeof attribs.step !== 'undefined'):
 | 
			
		||||
						if (attribs.ten && attribs.step == 2) {
 | 
			
		||||
							res.rounds[cur_game].events[attribs.who][cur_event].reach = decodeList(attribs.ten, true, false);
 | 
			
		||||
							res.rounds[cur_game].riichi.player.push(parseInt(attribs.who));
 | 
			
		||||
						} else if (attribs.step == 1) {
 | 
			
		||||
							res.rounds[cur_game].events[attribs.who][cur_event].reach = [];
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			onclosetag: function(tagname) {
 | 
			
		||||
				if (tagname === 'mjloggm') {
 | 
			
		||||
					res = replaceTiles(res);
 | 
			
		||||
					resolve(res);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}, {
 | 
			
		||||
			xmlMode: true,
 | 
			
		||||
			lowerCaseTags: true,
 | 
			
		||||
			lowerCaseAttributeNames: true
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		fs.readFile(file, function(err, data) {
 | 
			
		||||
			if (err) reject(err);
 | 
			
		||||
			parser.write(data);
 | 
			
		||||
		});
 | 
			
		||||
		parser.end();
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function workLog(req, res) {
 | 
			
		||||
	var url = require('url'),
 | 
			
		||||
		qs = require('querystring'),
 | 
			
		||||
		http = require('http'),
 | 
			
		||||
		fs = require('fs'),
 | 
			
		||||
		log, lang, json;
 | 
			
		||||
	if (req.method === 'POST') {
 | 
			
		||||
		var urlobj = url.parse(req.body.url),
 | 
			
		||||
			query = qs.parse(urlobj.query);
 | 
			
		||||
		log = query.log;
 | 
			
		||||
		lang = req.body.lang;
 | 
			
		||||
		json = req.body.json;
 | 
			
		||||
	} else if (req.method === 'GET') {
 | 
			
		||||
		log = req.query.log;
 | 
			
		||||
		lang = req.query.lang;
 | 
			
		||||
		json = req.query.json;
 | 
			
		||||
	}
 | 
			
		||||
	lang = (Object.keys(dict).indexOf(lang) >= 0 ? lang : 'en');
 | 
			
		||||
	if (!log) {
 | 
			
		||||
		res.status(200).send('Нужно передать URL на лог игры!!!');
 | 
			
		||||
	} else {
 | 
			
		||||
		var options = {
 | 
			
		||||
				host: 'e.mjv.jp',
 | 
			
		||||
				//host:  'e0.mjv.jp',
 | 
			
		||||
				port: 80,
 | 
			
		||||
				//followAllRedirects: true,
 | 
			
		||||
				path: '/0/log/plainfiles.cgi?' + log
 | 
			
		||||
				//path: '/0/log/?' + log
 | 
			
		||||
			},
 | 
			
		||||
			logfile = __dirname + '/tenhou/' + log + '.xml';
 | 
			
		||||
		if (!fs.existsSync(logfile)) {
 | 
			
		||||
			http.get(options, function(responce) {
 | 
			
		||||
				var body = '';
 | 
			
		||||
				responce.on('data', function(chunk) {
 | 
			
		||||
					body += chunk;
 | 
			
		||||
				});
 | 
			
		||||
				responce.on('end', function() {
 | 
			
		||||
					let fl = fs.openSync(logfile, 'wx');
 | 
			
		||||
					if (fl) {
 | 
			
		||||
						fs.writeSync(fl, body);
 | 
			
		||||
						fs.closeSync(fl);
 | 
			
		||||
					}
 | 
			
		||||
					if (fs.existsSync(logfile)) {
 | 
			
		||||
						parseLog(log, logfile, lang)
 | 
			
		||||
							.then(
 | 
			
		||||
								result =>
 | 
			
		||||
								(json === 1 ?
 | 
			
		||||
									res.status(200).json(result) : res.render('paifu', {
 | 
			
		||||
										data: result,
 | 
			
		||||
										str: dict[lang],
 | 
			
		||||
										lang: lang
 | 
			
		||||
									})),
 | 
			
		||||
								error => res.status(200).send('Error: ' + error)
 | 
			
		||||
							);
 | 
			
		||||
					}
 | 
			
		||||
				}).on('error', function(e) {
 | 
			
		||||
					log.error("Got error: " + e.message);
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		} else {
 | 
			
		||||
			parseLog(log, logfile, lang)
 | 
			
		||||
				.then(
 | 
			
		||||
					result => (json == 1 ?
 | 
			
		||||
						res.status(200).json(result) : res.render('paifu', {
 | 
			
		||||
							data: result,
 | 
			
		||||
							str: dict[lang],
 | 
			
		||||
							lang: lang
 | 
			
		||||
						})),
 | 
			
		||||
					//res.status(200).json(result),
 | 
			
		||||
					error => res.status(200).send('Error: ' + error)
 | 
			
		||||
				);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.index = function(req, res) {
 | 
			
		||||
	if (!req.query.log) {
 | 
			
		||||
		res.render('index', {
 | 
			
		||||
			error: req.flash('error')
 | 
			
		||||
		});
 | 
			
		||||
	} else {
 | 
			
		||||
		workLog(req, res);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.parse = workLog;
 | 
			
		||||
							
								
								
									
										341
									
								
								routes/translate.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								routes/translate.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,341 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
	ru: {
 | 
			
		||||
		title: 'Пайфу',
 | 
			
		||||
		link: 'Прямая ссылка на пайфу',
 | 
			
		||||
		player_info: 'Информация об игроках',
 | 
			
		||||
		name: 'Имя',
 | 
			
		||||
		sex: 'Пол',
 | 
			
		||||
		rate: 'Рейтинг',
 | 
			
		||||
		dan: 'Дан',
 | 
			
		||||
		sexF: 'жен.',
 | 
			
		||||
		sexM: 'муж.',
 | 
			
		||||
		sexC: 'комп',
 | 
			
		||||
		sex_: 'неизв.',
 | 
			
		||||
		log: 'Лог',
 | 
			
		||||
		dans: ['10 кю', '9 кю', '8 кю', '7 кю', '6 кю', '5 кю', '4 кю', '3 кю', '2 кю', '1 кю',
 | 
			
		||||
			'1 дан', '2 дан', '3 дан', '4 дан', '5 дан', '6 дан', '7 дан', '8 дан', '9 дан', '10 дан'
 | 
			
		||||
		],
 | 
			
		||||
		rounds: ['東1', '東2', '東3', '東4', '南1', '南2', '南3', '南4',
 | 
			
		||||
			'西1', '西2', '西3', '西4', '北1', '北2', '北3', '北4'
 | 
			
		||||
		],
 | 
			
		||||
		seat: ['東', '南', '西', '北'],
 | 
			
		||||
		round: 'Раунд',
 | 
			
		||||
		honba: 'Хонба',
 | 
			
		||||
		reach: 'Риичи',
 | 
			
		||||
		dora: 'Дора',
 | 
			
		||||
		urador: 'Урадора',
 | 
			
		||||
		start: 'Старт',
 | 
			
		||||
		draw: 'Взято',
 | 
			
		||||
		discard: 'Сброс',
 | 
			
		||||
		final: 'Финал',
 | 
			
		||||
		pon: 'Пон',
 | 
			
		||||
		chi: 'Чи',
 | 
			
		||||
		kan: 'Кан',
 | 
			
		||||
		chakan: 'Кан',
 | 
			
		||||
		nuki: 'Наки',
 | 
			
		||||
		ron: 'Рон',
 | 
			
		||||
		furikomi: 'Оплата',
 | 
			
		||||
		tsumo: 'Цумо',
 | 
			
		||||
		from: {
 | 
			
		||||
			M: 'выиграл с',
 | 
			
		||||
			F: 'выиграла с',
 | 
			
		||||
			C: 'выиграл с'
 | 
			
		||||
		},
 | 
			
		||||
		han: 'хан',
 | 
			
		||||
		fu: 'фу',
 | 
			
		||||
		score: 'Очков',
 | 
			
		||||
		yakulist: 'Список яку',
 | 
			
		||||
		ryuukyoku: {
 | 
			
		||||
			title: "Ничья",
 | 
			
		||||
			tenpai: "Темпай",
 | 
			
		||||
			noten: "Нотен",
 | 
			
		||||
			yao9: "9 терминалов и благородных",
 | 
			
		||||
			reach4: "Четыре риичи",
 | 
			
		||||
			ron3: "Тройной рон",
 | 
			
		||||
			kan4: "Четыре кана",
 | 
			
		||||
			kaze4: "Четыре ветра в сносе",
 | 
			
		||||
			nm: "Нагаши манган"
 | 
			
		||||
		},
 | 
			
		||||
		limits: ['', 'Манган', 'Ханеман', 'Байман', 'Санбайман', 'Якуман'],
 | 
			
		||||
		yaku: ['Мензенцумо',
 | 
			
		||||
			'Риичи',
 | 
			
		||||
			'Иппацу',
 | 
			
		||||
			'Чанкан',
 | 
			
		||||
			'Риншан кайхо',
 | 
			
		||||
			'Хайтей',
 | 
			
		||||
			'Хотей',
 | 
			
		||||
			'Пинфу',
 | 
			
		||||
			'Тан-яо',
 | 
			
		||||
			'Иипейко',
 | 
			
		||||
			'Свой восток',
 | 
			
		||||
			'Свой юг',
 | 
			
		||||
			'Свой запад',
 | 
			
		||||
			'Свой север',
 | 
			
		||||
			'Восток раунда',
 | 
			
		||||
			'Юг раунда',
 | 
			
		||||
			'Запад раунда',
 | 
			
		||||
			'Север раунда',
 | 
			
		||||
			'Белый дракон',
 | 
			
		||||
			'Зеленый дракон',
 | 
			
		||||
			'Красный дракон',
 | 
			
		||||
			'Дабл-риичи',
 | 
			
		||||
			'Чиитойцу',
 | 
			
		||||
			'Чанта',
 | 
			
		||||
			'Иццу',
 | 
			
		||||
			'Саншоку',
 | 
			
		||||
			'Саншоку доко',
 | 
			
		||||
			'Сууканцу',
 | 
			
		||||
			'Тойтой',
 | 
			
		||||
			'Сананко',
 | 
			
		||||
			'Шосанген',
 | 
			
		||||
			'Хонрото',
 | 
			
		||||
			'Рянпейко',
 | 
			
		||||
			'Джунчан',
 | 
			
		||||
			'Хоницу',
 | 
			
		||||
			'Чиницу',
 | 
			
		||||
			'Ренхо',
 | 
			
		||||
			'Тенхо',
 | 
			
		||||
			'Чихо',
 | 
			
		||||
			'Дайсанген',
 | 
			
		||||
			'Сууанко',
 | 
			
		||||
			'Сууанко танки',
 | 
			
		||||
			'Цууисо',
 | 
			
		||||
			'Рюисо',
 | 
			
		||||
			'Чинрото',
 | 
			
		||||
			'Чууренпото',
 | 
			
		||||
			'Чууренпото 9-стор',
 | 
			
		||||
			'Кокушимусо',
 | 
			
		||||
			'Кокушимусо 13-стор',
 | 
			
		||||
			'Дайсууши',
 | 
			
		||||
			'Шосууши',
 | 
			
		||||
			'Санканцу',
 | 
			
		||||
			'Дора',
 | 
			
		||||
			'Урадора',
 | 
			
		||||
			'Акадора'
 | 
			
		||||
		]
 | 
			
		||||
	},
 | 
			
		||||
	en: {
 | 
			
		||||
		title: 'Paifu',
 | 
			
		||||
		link: 'Direct paifu link',
 | 
			
		||||
		player_info: 'Player information',
 | 
			
		||||
		name: 'Name',
 | 
			
		||||
		sex: 'Sex',
 | 
			
		||||
		rate: 'Rating',
 | 
			
		||||
		dan: 'Dan',
 | 
			
		||||
		sexF: 'female',
 | 
			
		||||
		sexM: 'male',
 | 
			
		||||
		sexC: 'comp',
 | 
			
		||||
		sex_: 'NA',
 | 
			
		||||
		log: 'Log',
 | 
			
		||||
		dans: ['10 kyuu', '9 kyuu', '8 kyuu', '7 kyuu', '6 kyuu', '5 kyuu', '4 kyuu', '3 kyuu', '2 kyuu', '1 kyuu',
 | 
			
		||||
			'1 dan', '2 dan', '3 dan', '4 dan', '5 dan', '6 dan', '7 dan', '8 dan', '9 dan', '10 dan'
 | 
			
		||||
		],
 | 
			
		||||
		rounds: ['東1', '東2', '東3', '東4', '南1', '南2', '南3', '南4',
 | 
			
		||||
			'西1', '西2', '西3', '西4', '北1', '北2', '北3', '北4'
 | 
			
		||||
		],
 | 
			
		||||
		seat: ['東', '南', '西', '北'],
 | 
			
		||||
		round: 'Round',
 | 
			
		||||
		honba: 'Combo',
 | 
			
		||||
		reach: 'Reach',
 | 
			
		||||
		dora: 'Dora',
 | 
			
		||||
		urador: 'Uradora',
 | 
			
		||||
		start: 'Start',
 | 
			
		||||
		draw: 'Draw',
 | 
			
		||||
		discard: 'Drop',
 | 
			
		||||
		final: 'Final',
 | 
			
		||||
		pon: 'Pon',
 | 
			
		||||
		chi: 'Chii',
 | 
			
		||||
		kan: 'Kan',
 | 
			
		||||
		chakan: 'Kan',
 | 
			
		||||
		nuki: 'Nuki',
 | 
			
		||||
		ron: 'Ron',
 | 
			
		||||
		furikomi: 'Furikomi',
 | 
			
		||||
		tsumo: 'Tsumo',
 | 
			
		||||
		from: {
 | 
			
		||||
			M: 'win from',
 | 
			
		||||
			F: 'win from',
 | 
			
		||||
			C: 'win from'
 | 
			
		||||
		},
 | 
			
		||||
		han: 'han',
 | 
			
		||||
		fu: 'fu',
 | 
			
		||||
		score: 'Score',
 | 
			
		||||
		yakulist: 'Yaku list',
 | 
			
		||||
		ryuukyoku: {
 | 
			
		||||
			yao9: "9 ends",
 | 
			
		||||
			reach4: "Four riichi",
 | 
			
		||||
			ron3: "Triple ron",
 | 
			
		||||
			kan4: "Four kans",
 | 
			
		||||
			kaze4: "Wind discard",
 | 
			
		||||
			nm: "Nagashi mangan"
 | 
			
		||||
		},
 | 
			
		||||
		limits: ['', 'Mangan', 'haneman', 'baiman', 'sanbaiman', 'yakuman'],
 | 
			
		||||
		yaku: ['Mentsumo',
 | 
			
		||||
			'Riichi',
 | 
			
		||||
			'Ippatsu',
 | 
			
		||||
			'Chankan',
 | 
			
		||||
			'Rinshan kaihou',
 | 
			
		||||
			'Haitei raoyue',
 | 
			
		||||
			'Houtei raoyui',
 | 
			
		||||
			'Pinfu',
 | 
			
		||||
			'Tanyao',
 | 
			
		||||
			'Iipeiko',
 | 
			
		||||
			'Own east',
 | 
			
		||||
			'Own south',
 | 
			
		||||
			'Own west',
 | 
			
		||||
			'Own north',
 | 
			
		||||
			'Round east',
 | 
			
		||||
			'Round south',
 | 
			
		||||
			'Round west',
 | 
			
		||||
			'Round north',
 | 
			
		||||
			'White dragon',
 | 
			
		||||
			'Green dragon',
 | 
			
		||||
			'Red dragon',
 | 
			
		||||
			'Daburu riichi',
 | 
			
		||||
			'Chiitoitsu',
 | 
			
		||||
			'Chanta',
 | 
			
		||||
			'Ittsu',
 | 
			
		||||
			'Sanshoku doujun',
 | 
			
		||||
			'Sanshoku doukou',
 | 
			
		||||
			'Sankantsu',
 | 
			
		||||
			'Toitoi',
 | 
			
		||||
			'Sanankou',
 | 
			
		||||
			'Shousangen',
 | 
			
		||||
			'Honroutou',
 | 
			
		||||
			'Ryanpeikou',
 | 
			
		||||
			'Junchan',
 | 
			
		||||
			'Honitsu',
 | 
			
		||||
			'Chinitsu',
 | 
			
		||||
			'Renhou',
 | 
			
		||||
			'Tenhou',
 | 
			
		||||
			'Chihou',
 | 
			
		||||
			'Daisangen',
 | 
			
		||||
			'Suuankou',
 | 
			
		||||
			'Suuankou tanki',
 | 
			
		||||
			'Tsuuiisou',
 | 
			
		||||
			'Ryuuiisou',
 | 
			
		||||
			'Chinroutou',
 | 
			
		||||
			'Chuuren pouto',
 | 
			
		||||
			'Chuuren pouto 9-wait',
 | 
			
		||||
			'Kokushi musou',
 | 
			
		||||
			'Kokushi musou 13-wait',
 | 
			
		||||
			'Daisuushi',
 | 
			
		||||
			'Shousuushi',
 | 
			
		||||
			'Suukantsu',
 | 
			
		||||
			'Dora',
 | 
			
		||||
			'Uradora',
 | 
			
		||||
			'Akadora'
 | 
			
		||||
		]
 | 
			
		||||
	},
 | 
			
		||||
	jp: {
 | 
			
		||||
		title: 'Paifu',
 | 
			
		||||
		link: 'Direct paifu link',
 | 
			
		||||
		player_info: 'Player information',
 | 
			
		||||
		name: 'Name',
 | 
			
		||||
		sex: 'Sex',
 | 
			
		||||
		rate: 'Rating',
 | 
			
		||||
		dan: 'Dan',
 | 
			
		||||
		sexF: 'famale',
 | 
			
		||||
		sexM: 'male',
 | 
			
		||||
		sexC: 'comp',
 | 
			
		||||
		sex_: 'NA',
 | 
			
		||||
		log: 'Log',
 | 
			
		||||
		dans: ['新人', '9級', '8級', '7級', '6級', '5級', '4級', '3級', '2級', '1級',
 | 
			
		||||
			'初段', '二段', '三段', '四段', '五段', '六段', '七段', '八段', '九段', '十段', '天鳳位'
 | 
			
		||||
		],
 | 
			
		||||
		rounds: ['東1', '東2', '東3', '東4', '南1', '南2', '南3', '南4',
 | 
			
		||||
			'西1', '西2', '西3', '西4', '北1', '北2', '北3', '北4'
 | 
			
		||||
		],
 | 
			
		||||
		seat: ['東', '南', '西', '北'],
 | 
			
		||||
		round: 'Round',
 | 
			
		||||
		honba: 'Combo',
 | 
			
		||||
		reach: 'Reach',
 | 
			
		||||
		dora: 'Dora',
 | 
			
		||||
		urador: 'Uradora',
 | 
			
		||||
		start: 'Start',
 | 
			
		||||
		draw: 'Draw',
 | 
			
		||||
		discard: 'Drop',
 | 
			
		||||
		final: 'Final',
 | 
			
		||||
		pon: 'Pon',
 | 
			
		||||
		chi: 'Chii',
 | 
			
		||||
		kan: 'Kan',
 | 
			
		||||
		chakan: 'Kan',
 | 
			
		||||
		nuki: 'Nuki',
 | 
			
		||||
		ron: 'Ron',
 | 
			
		||||
		furikomi: 'フリコミ',
 | 
			
		||||
		tsumo: 'ツモ',
 | 
			
		||||
		from: {
 | 
			
		||||
			M: 'win from',
 | 
			
		||||
			F: 'win from',
 | 
			
		||||
			C: 'win from'
 | 
			
		||||
		},
 | 
			
		||||
		han: 'han',
 | 
			
		||||
		fu: 'fu',
 | 
			
		||||
		score: 'Score',
 | 
			
		||||
		yakulist: 'Yaku list',
 | 
			
		||||
		ryuukyoku: {
 | 
			
		||||
			yao9: "9 ends",
 | 
			
		||||
			reach4: "Four riichi",
 | 
			
		||||
			ron3: "Triple ron",
 | 
			
		||||
			kan4: "Four kans",
 | 
			
		||||
			kaze4: "Wind discard",
 | 
			
		||||
			nm: "Nagashi mangan"
 | 
			
		||||
		},
 | 
			
		||||
		limits: ['', 'Mangan', 'haneman', 'baiman', 'sanbaiman', 'yakuman'],
 | 
			
		||||
		yaku: ['門前清自摸和',
 | 
			
		||||
			'立直',
 | 
			
		||||
			'一発',
 | 
			
		||||
			'槍槓',
 | 
			
		||||
			'嶺上開花',
 | 
			
		||||
			'海底摸月',
 | 
			
		||||
			'河底撈魚',
 | 
			
		||||
			'平和',
 | 
			
		||||
			'断幺九',
 | 
			
		||||
			'一盃口',
 | 
			
		||||
			'自風 東',
 | 
			
		||||
			'自風 南',
 | 
			
		||||
			'自風 西',
 | 
			
		||||
			'自風 北',
 | 
			
		||||
			'場風 東',
 | 
			
		||||
			'場風 南',
 | 
			
		||||
			'場風 西',
 | 
			
		||||
			'場風 北',
 | 
			
		||||
			'役牌 白',
 | 
			
		||||
			'役牌 發',
 | 
			
		||||
			'役牌 中',
 | 
			
		||||
			'両立直',
 | 
			
		||||
			'七対子',
 | 
			
		||||
			'混全帯幺九',
 | 
			
		||||
			'一気通貫',
 | 
			
		||||
			'三色同順',
 | 
			
		||||
			'三色同刻',
 | 
			
		||||
			'三槓子',
 | 
			
		||||
			'対々和',
 | 
			
		||||
			'三暗刻',
 | 
			
		||||
			'小三元',
 | 
			
		||||
			'混老頭',
 | 
			
		||||
			'二盃口',
 | 
			
		||||
			'純全帯幺九',
 | 
			
		||||
			'混一色',
 | 
			
		||||
			'清一色',
 | 
			
		||||
			'人和',
 | 
			
		||||
			'天和',
 | 
			
		||||
			'地和',
 | 
			
		||||
			'大三元',
 | 
			
		||||
			'四暗刻',
 | 
			
		||||
			'四暗刻単騎',
 | 
			
		||||
			'字一色',
 | 
			
		||||
			'緑一色',
 | 
			
		||||
			'清老頭',
 | 
			
		||||
			'九蓮宝燈',
 | 
			
		||||
			'純正九蓮宝燈',
 | 
			
		||||
			'国士無双',
 | 
			
		||||
			'国士無双13面',
 | 
			
		||||
			'大四喜',
 | 
			
		||||
			'小四喜',
 | 
			
		||||
			'四槓子',
 | 
			
		||||
			'ドラ',
 | 
			
		||||
			'裏ドラ',
 | 
			
		||||
			'赤ドラ]'
 | 
			
		||||
		]
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user