wip
This commit is contained in:
parent
7a9434414d
commit
2d6f9b083f
|
@ -280,28 +280,18 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
|
||||||
|
|
||||||
room.gameState = Mmj.MasterGameEngine.createInitialState();
|
room.gameState = Mmj.MasterGameEngine.createInitialState();
|
||||||
room.isStarted = true;
|
room.isStarted = true;
|
||||||
|
|
||||||
await this.saveRoom(room);
|
await this.saveRoom(room);
|
||||||
|
|
||||||
this.globalEventService.publishMahjongRoomStream(room.id, 'started', { room: room });
|
this.globalEventService.publishMahjongRoomStream(room.id, 'started', { room: room });
|
||||||
|
|
||||||
return room;
|
this.kyokuStarted(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async packRoom(room: Room, me: MiUser) {
|
private kyokuStarted(room: Room) {
|
||||||
if (room.gameState) {
|
const mj = new Mmj.MasterGameEngine(room.gameState);
|
||||||
const mj = new Mmj.MasterGameEngine(room.gameState);
|
|
||||||
const myIndex = room.user1Id === me.id ? 1 : room.user2Id === me.id ? 2 : room.user3Id === me.id ? 3 : 4;
|
this.waitForTurn(room, mj.turn, mj);
|
||||||
return {
|
|
||||||
...room,
|
|
||||||
gameState: mj.createPlayerState(myIndex),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
...room,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -379,6 +369,17 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async nextKyoku(room: Room, mj: Mmj.MasterGameEngine) {
|
||||||
|
const res = mj.commit_nextKyoku();
|
||||||
|
room.gameState = mj.getState();
|
||||||
|
await this.saveRoom(room);
|
||||||
|
this.globalEventService.publishMahjongRoomStream(room.id, 'nextKyoku', {
|
||||||
|
room: room,
|
||||||
|
});
|
||||||
|
this.kyokuStarted(room);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async dahai(room: Room, mj: Mmj.MasterGameEngine, house: Mmj.House, tile: Mmj.TileId, riichi = false) {
|
private async dahai(room: Room, mj: Mmj.MasterGameEngine, house: Mmj.House, tile: Mmj.TileId, riichi = false) {
|
||||||
const res = mj.commit_dahai(house, tile, riichi);
|
const res = mj.commit_dahai(house, tile, riichi);
|
||||||
|
@ -551,6 +552,8 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
|
||||||
await this.saveRoom(room);
|
await this.saveRoom(room);
|
||||||
|
|
||||||
this.globalEventService.publishMahjongRoomStream(room.id, 'tsumoHora', { house: myHouse, handTiles: res.handTiles, tsumoTile: res.tsumoTile });
|
this.globalEventService.publishMahjongRoomStream(room.id, 'tsumoHora', { house: myHouse, handTiles: res.handTiles, tsumoTile: res.tsumoTile });
|
||||||
|
|
||||||
|
this.endKyoku(room, mj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -700,6 +703,27 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
|
||||||
await this.redisClient.del(`mahjong:gameTurnWaiting:${roomId}`);
|
await this.redisClient.del(`mahjong:gameTurnWaiting:${roomId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public packState(room: Room, me: MiUser) {
|
||||||
|
const mj = new Mmj.MasterGameEngine(room.gameState);
|
||||||
|
const myIndex = room.user1Id === me.id ? 1 : room.user2Id === me.id ? 2 : room.user3Id === me.id ? 3 : 4;
|
||||||
|
return mj.createPlayerState(myIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async packRoom(room: Room, me: MiUser) {
|
||||||
|
if (room.gameState) {
|
||||||
|
return {
|
||||||
|
...room,
|
||||||
|
gameState: this.packState(room, me),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
...room,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,11 @@ class MahjongRoomChannel extends Channel {
|
||||||
this.send('started', {
|
this.send('started', {
|
||||||
room: packed,
|
room: packed,
|
||||||
});
|
});
|
||||||
|
} else if (message.type === 'nextKyoku') {
|
||||||
|
const packed = this.mahjongService.packState(message.body.room, this.user!);
|
||||||
|
this.send('nextKyoku', {
|
||||||
|
state: packed,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.send(message.type, message.body);
|
this.send(message.type, message.body);
|
||||||
}
|
}
|
||||||
|
|
BIN
packages/frontend/assets/mahjong/kaisi.png
Normal file
BIN
packages/frontend/assets/mahjong/kaisi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
|
@ -40,12 +40,12 @@ import { computed } from 'vue';
|
||||||
import * as Mmj from 'misskey-mahjong';
|
import * as Mmj from 'misskey-mahjong';
|
||||||
|
|
||||||
//#region syntax suger
|
//#region syntax suger
|
||||||
function mj$(tileId: Mmj.TileId): Mmj.TileInstance {
|
function mj$(tid: Mmj.TileId): Mmj.TileInstance {
|
||||||
return Mmj.findTileByIdOrFail(tileId);
|
return Mmj.findTileByIdOrFail(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mj$type(tileId: Mmj.TileId): Mmj.TileType {
|
function mj$type(tid: Mmj.TileId): Mmj.TileType {
|
||||||
return mj$(tileId).t;
|
return mj$(tid).t;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div :class="$style.startTextContainer">
|
||||||
|
<Transition
|
||||||
|
:enterActiveClass="$style.transition_serif_enterActive"
|
||||||
|
:leaveActiveClass="$style.transition_serif_leaveActive"
|
||||||
|
:enterFromClass="$style.transition_serif_enterFrom"
|
||||||
|
:leaveToClass="$style.transition_serif_leaveTo"
|
||||||
|
>
|
||||||
|
<img v-if="startTextShowing" :src="`/client-assets/mahjong/kaisi.png`" style="display: block; width: 100%;"/>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div :class="$style.ryuukyokuContainer">
|
<div :class="$style.ryuukyokuContainer">
|
||||||
<Transition
|
<Transition
|
||||||
:enterActiveClass="$style.transition_serif_enterActive"
|
:enterActiveClass="$style.transition_serif_enterActive"
|
||||||
|
@ -210,6 +221,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>{{ res.pointDeltas.e }} / {{ res.pointDeltas.s }} / {{ res.pointDeltas.w }} / {{ res.pointDeltas.n }}</div>
|
<div>{{ res.pointDeltas.e }} / {{ res.pointDeltas.s }} / {{ res.pointDeltas.w }} / {{ res.pointDeltas.n }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<MkButton primary @click="confirmKyokuResult">OK</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -235,12 +247,12 @@ import * as os from '@/os.js';
|
||||||
import { confetti } from '@/scripts/confetti.js';
|
import { confetti } from '@/scripts/confetti.js';
|
||||||
|
|
||||||
//#region syntax suger
|
//#region syntax suger
|
||||||
function mj$(tileId: Mmj.TileId): Mmj.TileInstance {
|
function mj$(tid: Mmj.TileId): Mmj.TileInstance {
|
||||||
return Mmj.findTileByIdOrFail(tileId);
|
return Mmj.findTileByIdOrFail(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mj$type(tileId: Mmj.TileId): Mmj.TileType {
|
function mj$type(tid: Mmj.TileId): Mmj.TileType {
|
||||||
return mj$(tileId).t;
|
return mj$(tid).t;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -256,11 +268,11 @@ const myUserNumber = computed(() => room.value.user1Id === $i.id ? 1 : room.valu
|
||||||
const mj = shallowRef(new Mmj.PlayerGameEngine(myUserNumber.value, room.value.gameState));
|
const mj = shallowRef(new Mmj.PlayerGameEngine(myUserNumber.value, room.value.gameState));
|
||||||
|
|
||||||
const isMyTurn = computed(() => {
|
const isMyTurn = computed(() => {
|
||||||
return mj.value.state.turn === mj.value.myHouse;
|
return mj.value.turn === mj.value.myHouse;
|
||||||
});
|
});
|
||||||
|
|
||||||
const canHora = computed(() => {
|
const canHora = computed(() => {
|
||||||
return Mmj.getHoraSets(mj.value.myHandTiles).length > 0;
|
return Mmj.getHoraSets(mj.value.myHandTileTypes).length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectableTiles = ref<Mmj.TileType[] | null>(null);
|
const selectableTiles = ref<Mmj.TileType[] | null>(null);
|
||||||
|
@ -286,6 +298,7 @@ const kyokuResults = ref<Record<Mmj.House, {
|
||||||
n: null,
|
n: null,
|
||||||
});
|
});
|
||||||
const ryuukyokued = ref(false);
|
const ryuukyokued = ref(false);
|
||||||
|
const startTextShowing = ref(false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (room.value.isStarted && !room.value.isEnded) {
|
if (room.value.isStarted && !room.value.isEnded) {
|
||||||
|
@ -329,7 +342,7 @@ if (!props.room.isEnded) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function houseToUser(house: Mmj.House) {
|
function houseToUser(house: Mmj.House) {
|
||||||
return room.value.gameState.user1House === house ? room.value.user1 : room.value.gameState.user2House === house ? room.value.user2 : room.value.gameState.user3House === house ? room.value.user3 : room.value.user4;
|
return mj.value.user1House === house ? room.value.user1 : mj.value.user2House === house ? room.value.user2 : mj.value.user3House === house ? room.value.user3 : room.value.user4;
|
||||||
}
|
}
|
||||||
|
|
||||||
let riichiSelect = false;
|
let riichiSelect = false;
|
||||||
|
@ -434,6 +447,10 @@ function skip() {
|
||||||
props.connection!.send('nop', {});
|
props.connection!.send('nop', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function confirmKyokuResult() {
|
||||||
|
props.connection!.send('confirmNextKyoku', {});
|
||||||
|
}
|
||||||
|
|
||||||
function onStreamDahai(log) {
|
function onStreamDahai(log) {
|
||||||
console.log('onStreamDahai', log);
|
console.log('onStreamDahai', log);
|
||||||
|
|
||||||
|
@ -602,6 +619,10 @@ function onStreamRonned(log) {
|
||||||
|
|
||||||
for (const caller of log.callers) {
|
for (const caller of log.callers) {
|
||||||
ronSerifHouses[caller] = true;
|
ronSerifHouses[caller] = true;
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
ronSerifHouses[caller] = false;
|
||||||
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('ronned', res);
|
console.log('ronned', res);
|
||||||
|
@ -620,6 +641,9 @@ function onStreamTsumoHora(log) {
|
||||||
}, 1500);
|
}, 1500);
|
||||||
|
|
||||||
tsumoSerifHouses[log.house] = true;
|
tsumoSerifHouses[log.house] = true;
|
||||||
|
window.setTimeout(() => {
|
||||||
|
tsumoSerifHouses[log.house] = false;
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
console.log('tsumohora', res);
|
console.log('tsumohora', res);
|
||||||
}
|
}
|
||||||
|
@ -634,6 +658,23 @@ function onStreamRyuukyoku(log) {
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onStreamNextKyoku(log) {
|
||||||
|
console.log('onStreamNextKyoku', log);
|
||||||
|
|
||||||
|
const res = mj.value.commit_nextKyoku(log.state);
|
||||||
|
triggerRef(mj);
|
||||||
|
|
||||||
|
iTsumoed.value = false;
|
||||||
|
showKyokuResults.value = false;
|
||||||
|
kyokuResults.value = {
|
||||||
|
e: null,
|
||||||
|
s: null,
|
||||||
|
w: null,
|
||||||
|
n: null,
|
||||||
|
};
|
||||||
|
ryuukyokued.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
function restoreRoom(_room) {
|
function restoreRoom(_room) {
|
||||||
room.value = deepClone(_room);
|
room.value = deepClone(_room);
|
||||||
|
|
||||||
|
@ -653,6 +694,7 @@ onMounted(() => {
|
||||||
props.connection.on('ronned', onStreamRonned);
|
props.connection.on('ronned', onStreamRonned);
|
||||||
props.connection.on('tsumoHora', onStreamTsumoHora);
|
props.connection.on('tsumoHora', onStreamTsumoHora);
|
||||||
props.connection.on('ryuukyoku', onStreamRyuukyoku);
|
props.connection.on('ryuukyoku', onStreamRyuukyoku);
|
||||||
|
props.connection.on('nextKyoku', onStreamNextKyoku);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -669,6 +711,7 @@ onActivated(() => {
|
||||||
props.connection.on('ronned', onStreamRonned);
|
props.connection.on('ronned', onStreamRonned);
|
||||||
props.connection.on('tsumoHora', onStreamTsumoHora);
|
props.connection.on('tsumoHora', onStreamTsumoHora);
|
||||||
props.connection.on('ryuukyoku', onStreamRyuukyoku);
|
props.connection.on('ryuukyoku', onStreamRyuukyoku);
|
||||||
|
props.connection.on('nextKyoku', onStreamNextKyoku);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -685,6 +728,7 @@ onDeactivated(() => {
|
||||||
props.connection.off('ronned', onStreamRonned);
|
props.connection.off('ronned', onStreamRonned);
|
||||||
props.connection.off('tsumoHora', onStreamTsumoHora);
|
props.connection.off('tsumoHora', onStreamTsumoHora);
|
||||||
props.connection.off('ryuukyoku', onStreamRyuukyoku);
|
props.connection.off('ryuukyoku', onStreamRyuukyoku);
|
||||||
|
props.connection.off('nextKyoku', onStreamNextKyoku);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -701,6 +745,7 @@ onUnmounted(() => {
|
||||||
props.connection.off('ronned', onStreamRonned);
|
props.connection.off('ronned', onStreamRonned);
|
||||||
props.connection.off('tsumoHora', onStreamTsumoHora);
|
props.connection.off('tsumoHora', onStreamTsumoHora);
|
||||||
props.connection.off('ryuukyoku', onStreamRyuukyoku);
|
props.connection.off('ryuukyoku', onStreamRyuukyoku);
|
||||||
|
props.connection.off('nextKyoku', onStreamNextKyoku);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -92,14 +92,14 @@ export const TILE_ID_MAP = new Map<TileId, TileInstance>([
|
||||||
/* eslint-enable no-multi-spaces */
|
/* eslint-enable no-multi-spaces */
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function findTileByIdOrFail(tileId: TileId): TileInstance {
|
export function findTileByIdOrFail(tid: TileId): TileInstance {
|
||||||
const tile = TILE_ID_MAP.get(tileId);
|
const tile = TILE_ID_MAP.get(tid);
|
||||||
if (tile == null) throw new Error(`tile not found: ${tileId}`);
|
if (tile == null) throw new Error(`tile not found: ${tid}`);
|
||||||
return tile;
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findTileById(tileId: TileId): TileInstance | null {
|
export function findTileById(tid: TileId): TileInstance | null {
|
||||||
return TILE_ID_MAP.get(tileId) ?? null;
|
return TILE_ID_MAP.get(tid) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type House = 'e' | 's' | 'w' | 'n';
|
export type House = 'e' | 's' | 'w' | 'n';
|
||||||
|
@ -300,6 +300,13 @@ export const YAKU_DEFINITIONS = [{
|
||||||
calc: (state: EnvForCalcYaku) => {
|
calc: (state: EnvForCalcYaku) => {
|
||||||
return state.tsumoTile != null;
|
return state.tsumoTile != null;
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
name: 'ippatsu',
|
||||||
|
fan: 1,
|
||||||
|
isYakuman: false,
|
||||||
|
calc: (state: EnvForCalcYaku) => {
|
||||||
|
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
name: 'red',
|
name: 'red',
|
||||||
fan: 1,
|
fan: 1,
|
||||||
|
|
|
@ -9,12 +9,12 @@ import * as Common from './common.js';
|
||||||
import { PlayerState } from './engine.player.js';
|
import { PlayerState } from './engine.player.js';
|
||||||
|
|
||||||
//#region syntax suger
|
//#region syntax suger
|
||||||
function $(tileId: TileId): Common.TileInstance {
|
function $(tid: TileId): Common.TileInstance {
|
||||||
return Common.findTileByIdOrFail(tileId);
|
return Common.findTileByIdOrFail(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function $type(tileId: TileId): TileType {
|
function $type(tid: TileId): TileType {
|
||||||
return $(tileId).t;
|
return $(tid).t;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ export type MasterState = {
|
||||||
|
|
||||||
round: 'e' | 's' | 'w' | 'n';
|
round: 'e' | 's' | 'w' | 'n';
|
||||||
kyoku: number;
|
kyoku: number;
|
||||||
|
turnCount: number;
|
||||||
tiles: TileId[];
|
tiles: TileId[];
|
||||||
kingTiles: TileId[];
|
kingTiles: TileId[];
|
||||||
activatedDorasCount: number;
|
activatedDorasCount: number;
|
||||||
|
@ -59,6 +59,12 @@ export type MasterState = {
|
||||||
w: boolean;
|
w: boolean;
|
||||||
n: boolean;
|
n: boolean;
|
||||||
};
|
};
|
||||||
|
ippatsus: {
|
||||||
|
e: boolean;
|
||||||
|
s: boolean;
|
||||||
|
w: boolean;
|
||||||
|
n: boolean;
|
||||||
|
};
|
||||||
points: {
|
points: {
|
||||||
e: number;
|
e: number;
|
||||||
s: number;
|
s: number;
|
||||||
|
@ -176,6 +182,10 @@ export class MasterGameEngine {
|
||||||
return this.state.user4House;
|
return this.state.user4House;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get turn(): House | null {
|
||||||
|
return this.state.turn;
|
||||||
|
}
|
||||||
|
|
||||||
public static createInitialState(): MasterState {
|
public static createInitialState(): MasterState {
|
||||||
const ikasama: TileId[] = [125, 129, 9, 56, 57, 61, 77, 81, 85, 133, 134, 135, 121, 122];
|
const ikasama: TileId[] = [125, 129, 9, 56, 57, 61, 77, 81, 85, 133, 134, 135, 121, 122];
|
||||||
|
|
||||||
|
@ -201,6 +211,7 @@ export class MasterGameEngine {
|
||||||
user4House: 'n',
|
user4House: 'n',
|
||||||
round: 'e',
|
round: 'e',
|
||||||
kyoku: 1,
|
kyoku: 1,
|
||||||
|
turnCount: 0,
|
||||||
tiles,
|
tiles,
|
||||||
kingTiles,
|
kingTiles,
|
||||||
activatedDorasCount: 1,
|
activatedDorasCount: 1,
|
||||||
|
@ -253,12 +264,12 @@ export class MasterGameEngine {
|
||||||
return tile;
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private canRon(house: House, tileId: TileId): boolean {
|
private canRon(house: House, tid: TileId): boolean {
|
||||||
// フリテン
|
// フリテン
|
||||||
// TODO: ポンされるなどして自分の河にない場合の考慮
|
// TODO: ポンされるなどして自分の河にない場合の考慮
|
||||||
if (this.hoTileTypes[house].includes($type(tileId))) return false;
|
if (this.hoTileTypes[house].includes($type(tid))) return false;
|
||||||
|
|
||||||
const horaSets = Common.getHoraSets(this.handTileTypes[house].concat($type(tileId)));
|
const horaSets = Common.getHoraSets(this.handTileTypes[house].concat($type(tid)));
|
||||||
if (horaSets.length === 0) return false; // 完成形じゃない
|
if (horaSets.length === 0) return false; // 完成形じゃない
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -268,15 +279,19 @@ export class MasterGameEngine {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private canPon(house: House, tileId: TileId): boolean {
|
private canPon(house: House, tid: TileId): boolean {
|
||||||
return this.handTileTypes[house].filter(t => t === $type(tileId)).length === 2;
|
return this.handTileTypes[house].filter(t => t === $type(tid)).length === 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private canCii(caller: House, callee: House, tileId: TileId): boolean {
|
private canDaiminkan(caller: House, tid: TileId): boolean {
|
||||||
|
return this.handTileTypes[caller].filter(t => t === $type(tid)).length === 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private canCii(caller: House, callee: House, tid: TileId): boolean {
|
||||||
if (callee !== Common.prevHouse(caller)) return false;
|
if (callee !== Common.prevHouse(caller)) return false;
|
||||||
const hand = this.handTileTypes[caller];
|
const hand = this.handTileTypes[caller];
|
||||||
return Common.SHUNTU_PATTERNS.some(pattern =>
|
return Common.SHUNTU_PATTERNS.some(pattern =>
|
||||||
pattern.includes($type(tileId)) &&
|
pattern.includes($type(tid)) &&
|
||||||
pattern.filter(t => hand.includes(t)).length >= 2);
|
pattern.filter(t => hand.includes(t)).length >= 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,68 +340,104 @@ export class MasterGameEngine {
|
||||||
this.endKyoku();
|
this.endKyoku();
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit_dahai(house: House, tileId: TileId, riichi = false) {
|
public startTransaction() {
|
||||||
|
const newState = structuredClone(this.state);
|
||||||
|
return {
|
||||||
|
state: newState,
|
||||||
|
commit: () => {
|
||||||
|
this.state = newState;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public commit_nextKyoku() {
|
||||||
|
const newState = MasterGameEngine.createInitialState();
|
||||||
|
newState.kyoku = this.state.kyoku + 1;
|
||||||
|
newState.points = this.state.points;
|
||||||
|
newState.turn = 'e';
|
||||||
|
newState.user1House = Common.nextHouse(this.state.user1House);
|
||||||
|
newState.user2House = Common.nextHouse(this.state.user2House);
|
||||||
|
newState.user3House = Common.nextHouse(this.state.user3House);
|
||||||
|
newState.user4House = Common.nextHouse(this.state.user4House);
|
||||||
|
this.state = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public commit_dahai(house: House, tid: TileId, riichi = false) {
|
||||||
|
const { state, commit } = this.startTransaction();
|
||||||
|
|
||||||
if (this.state.turn !== house) throw new Error('Not your turn');
|
if (this.state.turn !== house) throw new Error('Not your turn');
|
||||||
|
|
||||||
if (riichi) {
|
if (riichi) {
|
||||||
|
if (this.state.riichis[house]) throw new Error('Already riichi');
|
||||||
const tempHandTiles = [...this.handTileTypes[house]];
|
const tempHandTiles = [...this.handTileTypes[house]];
|
||||||
tempHandTiles.splice(tempHandTiles.indexOf($type(tileId)), 1);
|
tempHandTiles.splice(tempHandTiles.indexOf($type(tid)), 1);
|
||||||
if (Common.getHoraTiles(tempHandTiles).length === 0) throw new Error('Not tenpai');
|
if (Common.getHoraTiles(tempHandTiles).length === 0) throw new Error('Not tenpai');
|
||||||
if (this.state.points[house] < 1000) throw new Error('Not enough points');
|
if (this.state.points[house] < 1000) throw new Error('Not enough points');
|
||||||
}
|
}
|
||||||
|
|
||||||
const handTiles = this.state.handTiles[house];
|
const handTiles = this.state.handTiles[house];
|
||||||
if (!handTiles.includes(tileId)) throw new Error('No such tile in your hand');
|
if (!handTiles.includes(tid)) throw new Error('No such tile in your hand');
|
||||||
handTiles.splice(handTiles.indexOf(tileId), 1);
|
handTiles.splice(handTiles.indexOf(tid), 1);
|
||||||
this.state.hoTiles[house].push(tileId);
|
this.state.hoTiles[house].push(tid);
|
||||||
|
|
||||||
|
if (this.state.riichis[house]) {
|
||||||
|
this.state.ippatsus[house] = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (riichi) {
|
if (riichi) {
|
||||||
this.state.riichis[house] = true;
|
this.state.riichis[house] = true;
|
||||||
|
this.state.ippatsus[house] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canRonHouses: House[] = [];
|
const canRonHouses: House[] = [];
|
||||||
switch (house) {
|
switch (house) {
|
||||||
case 'e':
|
case 'e':
|
||||||
if (this.canRon('s', tileId)) canRonHouses.push('s');
|
if (this.canRon('s', tid)) canRonHouses.push('s');
|
||||||
if (this.canRon('w', tileId)) canRonHouses.push('w');
|
if (this.canRon('w', tid)) canRonHouses.push('w');
|
||||||
if (this.canRon('n', tileId)) canRonHouses.push('n');
|
if (this.canRon('n', tid)) canRonHouses.push('n');
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if (this.canRon('e', tileId)) canRonHouses.push('e');
|
if (this.canRon('e', tid)) canRonHouses.push('e');
|
||||||
if (this.canRon('w', tileId)) canRonHouses.push('w');
|
if (this.canRon('w', tid)) canRonHouses.push('w');
|
||||||
if (this.canRon('n', tileId)) canRonHouses.push('n');
|
if (this.canRon('n', tid)) canRonHouses.push('n');
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
if (this.canRon('e', tileId)) canRonHouses.push('e');
|
if (this.canRon('e', tid)) canRonHouses.push('e');
|
||||||
if (this.canRon('s', tileId)) canRonHouses.push('s');
|
if (this.canRon('s', tid)) canRonHouses.push('s');
|
||||||
if (this.canRon('n', tileId)) canRonHouses.push('n');
|
if (this.canRon('n', tid)) canRonHouses.push('n');
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
if (this.canRon('e', tileId)) canRonHouses.push('e');
|
if (this.canRon('e', tid)) canRonHouses.push('e');
|
||||||
if (this.canRon('s', tileId)) canRonHouses.push('s');
|
if (this.canRon('s', tid)) canRonHouses.push('s');
|
||||||
if (this.canRon('w', tileId)) canRonHouses.push('w');
|
if (this.canRon('w', tid)) canRonHouses.push('w');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canKanHouse: House | null = null;
|
let canKanHouse: House | null = null;
|
||||||
|
switch (house) {
|
||||||
|
case 'e': canKanHouse = this.canDaiminkan('s', tid) ? 's' : this.canDaiminkan('w', tid) ? 'w' : this.canDaiminkan('n', tid) ? 'n' : null; break;
|
||||||
|
case 's': canKanHouse = this.canDaiminkan('e', tid) ? 'e' : this.canDaiminkan('w', tid) ? 'w' : this.canDaiminkan('n', tid) ? 'n' : null; break;
|
||||||
|
case 'w': canKanHouse = this.canDaiminkan('e', tid) ? 'e' : this.canDaiminkan('s', tid) ? 's' : this.canDaiminkan('n', tid) ? 'n' : null; break;
|
||||||
|
case 'n': canKanHouse = this.canDaiminkan('e', tid) ? 'e' : this.canDaiminkan('s', tid) ? 's' : this.canDaiminkan('w', tid) ? 'w' : null; break;
|
||||||
|
}
|
||||||
|
|
||||||
let canPonHouse: House | null = null;
|
let canPonHouse: House | null = null;
|
||||||
switch (house) {
|
switch (house) {
|
||||||
case 'e': canPonHouse = this.canPon('s', tileId) ? 's' : this.canPon('w', tileId) ? 'w' : this.canPon('n', tileId) ? 'n' : null; break;
|
case 'e': canPonHouse = this.canPon('s', tid) ? 's' : this.canPon('w', tid) ? 'w' : this.canPon('n', tid) ? 'n' : null; break;
|
||||||
case 's': canPonHouse = this.canPon('e', tileId) ? 'e' : this.canPon('w', tileId) ? 'w' : this.canPon('n', tileId) ? 'n' : null; break;
|
case 's': canPonHouse = this.canPon('e', tid) ? 'e' : this.canPon('w', tid) ? 'w' : this.canPon('n', tid) ? 'n' : null; break;
|
||||||
case 'w': canPonHouse = this.canPon('e', tileId) ? 'e' : this.canPon('s', tileId) ? 's' : this.canPon('n', tileId) ? 'n' : null; break;
|
case 'w': canPonHouse = this.canPon('e', tid) ? 'e' : this.canPon('s', tid) ? 's' : this.canPon('n', tid) ? 'n' : null; break;
|
||||||
case 'n': canPonHouse = this.canPon('e', tileId) ? 'e' : this.canPon('s', tileId) ? 's' : this.canPon('w', tileId) ? 'w' : null; break;
|
case 'n': canPonHouse = this.canPon('e', tid) ? 'e' : this.canPon('s', tid) ? 's' : this.canPon('w', tid) ? 'w' : null; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let canCiiHouse: House | null = null;
|
let canCiiHouse: House | null = null;
|
||||||
switch (house) {
|
switch (house) {
|
||||||
case 'e': canCiiHouse = this.canCii('s', house, tileId) ? 's' : this.canCii('w', house, tileId) ? 'w' : this.canCii('n', house, tileId) ? 'n' : null; break;
|
case 'e': canCiiHouse = this.canCii('s', house, tid) ? 's' : this.canCii('w', house, tid) ? 'w' : this.canCii('n', house, tid) ? 'n' : null; break;
|
||||||
case 's': canCiiHouse = this.canCii('e', house, tileId) ? 'e' : this.canCii('w', house, tileId) ? 'w' : this.canCii('n', house, tileId) ? 'n' : null; break;
|
case 's': canCiiHouse = this.canCii('e', house, tid) ? 'e' : this.canCii('w', house, tid) ? 'w' : this.canCii('n', house, tid) ? 'n' : null; break;
|
||||||
case 'w': canCiiHouse = this.canCii('e', house, tileId) ? 'e' : this.canCii('s', house, tileId) ? 's' : this.canCii('n', house, tileId) ? 'n' : null; break;
|
case 'w': canCiiHouse = this.canCii('e', house, tid) ? 'e' : this.canCii('s', house, tid) ? 's' : this.canCii('n', house, tid) ? 'n' : null; break;
|
||||||
case 'n': canCiiHouse = this.canCii('e', house, tileId) ? 'e' : this.canCii('s', house, tileId) ? 's' : this.canCii('w', house, tileId) ? 'w' : null; break;
|
case 'n': canCiiHouse = this.canCii('e', house, tid) ? 'e' : this.canCii('s', house, tid) ? 's' : this.canCii('w', house, tid) ? 'w' : null; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canRonHouses.length > 0 || canPonHouse != null || canCiiHouse != null) {
|
if (canRonHouses.length > 0 || canKanHouse != null || canPonHouse != null || canCiiHouse != null) {
|
||||||
if (canRonHouses.length > 0) {
|
if (canRonHouses.length > 0) {
|
||||||
this.state.askings.ron = {
|
this.state.askings.ron = {
|
||||||
callee: house,
|
callee: house,
|
||||||
|
@ -445,13 +496,18 @@ export class MasterGameEngine {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit_kakan(house: House, tileId: TileId) {
|
public commit_kakan(house: House, tid: TileId) {
|
||||||
const pon = this.state.huros[house].find(h => h.type === 'pon' && $type(h.tiles[0]) === $type(tileId));
|
const pon = this.state.huros[house].find(h => h.type === 'pon' && $type(h.tiles[0]) === $type(tid));
|
||||||
if (pon == null) throw new Error('No such pon');
|
if (pon == null) throw new Error('No such pon');
|
||||||
this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(tileId), 1);
|
this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(tid), 1);
|
||||||
const tiles = [tileId, ...pon.tiles];
|
const tiles = [tid, ...pon.tiles];
|
||||||
this.state.huros[house].push({ type: 'minkan', tiles: tiles, from: pon.from });
|
this.state.huros[house].push({ type: 'minkan', tiles: tiles, from: pon.from });
|
||||||
|
|
||||||
|
this.state.ippatsus.e = false;
|
||||||
|
this.state.ippatsus.s = false;
|
||||||
|
this.state.ippatsus.w = false;
|
||||||
|
this.state.ippatsus.n = false;
|
||||||
|
|
||||||
this.state.activatedDorasCount++;
|
this.state.activatedDorasCount++;
|
||||||
|
|
||||||
const rinsyan = this.tsumo();
|
const rinsyan = this.tsumo();
|
||||||
|
@ -463,14 +519,14 @@ export class MasterGameEngine {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit_ankan(house: House, tileId: TileId) {
|
public commit_ankan(house: House, tid: TileId) {
|
||||||
const t1 = this.state.handTiles[house].filter(t => $type(t) === $type(tileId)).at(0);
|
const t1 = this.state.handTiles[house].filter(t => $type(t) === $type(tid)).at(0);
|
||||||
if (t1 == null) throw new Error('No such tile');
|
if (t1 == null) throw new Error('No such tile');
|
||||||
const t2 = this.state.handTiles[house].filter(t => $type(t) === $type(tileId)).at(1);
|
const t2 = this.state.handTiles[house].filter(t => $type(t) === $type(tid)).at(1);
|
||||||
if (t2 == null) throw new Error('No such tile');
|
if (t2 == null) throw new Error('No such tile');
|
||||||
const t3 = this.state.handTiles[house].filter(t => $type(t) === $type(tileId)).at(2);
|
const t3 = this.state.handTiles[house].filter(t => $type(t) === $type(tid)).at(2);
|
||||||
if (t3 == null) throw new Error('No such tile');
|
if (t3 == null) throw new Error('No such tile');
|
||||||
const t4 = this.state.handTiles[house].filter(t => $type(t) === $type(tileId)).at(3);
|
const t4 = this.state.handTiles[house].filter(t => $type(t) === $type(tid)).at(3);
|
||||||
if (t4 == null) throw new Error('No such tile');
|
if (t4 == null) throw new Error('No such tile');
|
||||||
this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(t1), 1);
|
this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(t1), 1);
|
||||||
this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(t2), 1);
|
this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(t2), 1);
|
||||||
|
@ -479,6 +535,11 @@ export class MasterGameEngine {
|
||||||
const tiles = [t1, t2, t3, t4];
|
const tiles = [t1, t2, t3, t4];
|
||||||
this.state.huros[house].push({ type: 'ankan', tiles: tiles });
|
this.state.huros[house].push({ type: 'ankan', tiles: tiles });
|
||||||
|
|
||||||
|
this.state.ippatsus.e = false;
|
||||||
|
this.state.ippatsus.s = false;
|
||||||
|
this.state.ippatsus.w = false;
|
||||||
|
this.state.ippatsus.n = false;
|
||||||
|
|
||||||
this.state.activatedDorasCount++;
|
this.state.activatedDorasCount++;
|
||||||
|
|
||||||
const rinsyan = this.tsumo();
|
const rinsyan = this.tsumo();
|
||||||
|
@ -567,6 +628,11 @@ export class MasterGameEngine {
|
||||||
const tiles = [tile, t1, t2, t3];
|
const tiles = [tile, t1, t2, t3];
|
||||||
this.state.huros[kan.caller].push({ type: 'minkan', tiles: tiles, from: kan.callee });
|
this.state.huros[kan.caller].push({ type: 'minkan', tiles: tiles, from: kan.callee });
|
||||||
|
|
||||||
|
this.state.ippatsus.e = false;
|
||||||
|
this.state.ippatsus.s = false;
|
||||||
|
this.state.ippatsus.w = false;
|
||||||
|
this.state.ippatsus.n = false;
|
||||||
|
|
||||||
this.state.activatedDorasCount++;
|
this.state.activatedDorasCount++;
|
||||||
|
|
||||||
const rinsyan = this.tsumo();
|
const rinsyan = this.tsumo();
|
||||||
|
@ -594,6 +660,11 @@ export class MasterGameEngine {
|
||||||
const tiles = [tile, t1, t2];
|
const tiles = [tile, t1, t2];
|
||||||
this.state.huros[pon.caller].push({ type: 'pon', tiles: tiles, from: pon.callee });
|
this.state.huros[pon.caller].push({ type: 'pon', tiles: tiles, from: pon.callee });
|
||||||
|
|
||||||
|
this.state.ippatsus.e = false;
|
||||||
|
this.state.ippatsus.s = false;
|
||||||
|
this.state.ippatsus.w = false;
|
||||||
|
this.state.ippatsus.n = false;
|
||||||
|
|
||||||
this.state.turn = pon.caller;
|
this.state.turn = pon.caller;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -654,6 +725,11 @@ export class MasterGameEngine {
|
||||||
|
|
||||||
this.state.huros[cii.caller].push({ type: 'cii', tiles: tiles, from: cii.callee });
|
this.state.huros[cii.caller].push({ type: 'cii', tiles: tiles, from: cii.callee });
|
||||||
|
|
||||||
|
this.state.ippatsus.e = false;
|
||||||
|
this.state.ippatsus.s = false;
|
||||||
|
this.state.ippatsus.w = false;
|
||||||
|
this.state.ippatsus.n = false;
|
||||||
|
|
||||||
this.state.turn = cii.caller;
|
this.state.turn = cii.caller;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -699,6 +775,7 @@ export class MasterGameEngine {
|
||||||
user4House: this.state.user4House,
|
user4House: this.state.user4House,
|
||||||
round: this.state.round,
|
round: this.state.round,
|
||||||
kyoku: this.state.kyoku,
|
kyoku: this.state.kyoku,
|
||||||
|
turnCount: this.state.turnCount,
|
||||||
tilesCount: this.state.tiles.length,
|
tilesCount: this.state.tiles.length,
|
||||||
doraIndicateTiles: this.state.kingTiles.slice(0, this.state.activatedDorasCount),
|
doraIndicateTiles: this.state.kingTiles.slice(0, this.state.activatedDorasCount),
|
||||||
handTiles: {
|
handTiles: {
|
||||||
|
@ -756,3 +833,7 @@ export class MasterGameEngine {
|
||||||
return structuredClone(this.state);
|
return structuredClone(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function commit_dahai(state: MasterState): MasterState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -8,12 +8,12 @@ import { TileType, House, Huro, TileId, YAKU_DEFINITIONS } from './common.js';
|
||||||
import * as Common from './common.js';
|
import * as Common from './common.js';
|
||||||
|
|
||||||
//#region syntax suger
|
//#region syntax suger
|
||||||
function $(tileId: TileId): Common.TileInstance {
|
function $(tid: TileId): Common.TileInstance {
|
||||||
return Common.findTileByIdOrFail(tileId);
|
return Common.findTileByIdOrFail(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function $type(tileId: TileId): TileType {
|
function $type(tid: TileId): TileType {
|
||||||
return $(tileId).t;
|
return $(tid).t;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ export type PlayerState = {
|
||||||
round: 'e' | 's' | 'w' | 'n';
|
round: 'e' | 's' | 'w' | 'n';
|
||||||
kyoku: number;
|
kyoku: number;
|
||||||
|
|
||||||
|
turnCount: number;
|
||||||
tilesCount: number;
|
tilesCount: number;
|
||||||
doraIndicateTiles: TileId[];
|
doraIndicateTiles: TileId[];
|
||||||
|
|
||||||
|
@ -111,6 +112,10 @@ export class PlayerGameEngine {
|
||||||
return this.state.huros;
|
return this.state.huros;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get turnCount(): number {
|
||||||
|
return this.state.turnCount;
|
||||||
|
}
|
||||||
|
|
||||||
public get tilesCount(): number {
|
public get tilesCount(): number {
|
||||||
return this.state.tilesCount;
|
return this.state.tilesCount;
|
||||||
}
|
}
|
||||||
|
@ -131,6 +136,26 @@ export class PlayerGameEngine {
|
||||||
return this.state.canCii;
|
return this.state.canCii;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get turn(): House | null {
|
||||||
|
return this.state.turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get user1House(): House {
|
||||||
|
return this.state.user1House;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get user2House(): House {
|
||||||
|
return this.state.user2House;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get user3House(): House {
|
||||||
|
return this.state.user3House;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get user4House(): House {
|
||||||
|
return this.state.user4House;
|
||||||
|
}
|
||||||
|
|
||||||
public get myHouse(): House {
|
public get myHouse(): House {
|
||||||
switch (this.myUserNumber) {
|
switch (this.myUserNumber) {
|
||||||
case 1: return this.state.user1House;
|
case 1: return this.state.user1House;
|
||||||
|
@ -152,19 +177,23 @@ export class PlayerGameEngine {
|
||||||
return this.state.riichis[this.myHouse];
|
return this.state.riichis[this.myHouse];
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit_tsumo(house: House, tileId: TileId) {
|
public commit_nextKyoku(state: PlayerState) {
|
||||||
console.log('commit_tsumo', this.state.turn, house, tileId);
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public commit_tsumo(house: House, tid: TileId) {
|
||||||
|
console.log('commit_tsumo', this.state.turn, house, tid);
|
||||||
this.state.tilesCount--;
|
this.state.tilesCount--;
|
||||||
this.state.turn = house;
|
this.state.turn = house;
|
||||||
if (house === this.myHouse) {
|
if (house === this.myHouse) {
|
||||||
this.myHandTiles.push(tileId);
|
this.myHandTiles.push(tid);
|
||||||
} else {
|
} else {
|
||||||
this.state.handTiles[house].push(0);
|
this.state.handTiles[house].push(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit_dahai(house: House, tileId: TileId, riichi = false) {
|
public commit_dahai(house: House, tid: TileId, riichi = false) {
|
||||||
console.log('commit_dahai', this.state.turn, house, tileId, riichi);
|
console.log('commit_dahai', this.state.turn, house, tid, riichi);
|
||||||
if (this.state.turn !== house) throw new PlayerGameEngine.InvalidOperationError();
|
if (this.state.turn !== house) throw new PlayerGameEngine.InvalidOperationError();
|
||||||
|
|
||||||
if (riichi) {
|
if (riichi) {
|
||||||
|
@ -172,23 +201,23 @@ export class PlayerGameEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (house === this.myHouse) {
|
if (house === this.myHouse) {
|
||||||
this.myHandTiles.splice(this.myHandTiles.indexOf(tileId), 1);
|
this.myHandTiles.splice(this.myHandTiles.indexOf(tid), 1);
|
||||||
this.state.hoTiles[this.myHouse].push(tileId);
|
this.state.hoTiles[this.myHouse].push(tid);
|
||||||
} else {
|
} else {
|
||||||
this.state.handTiles[house].pop();
|
this.state.handTiles[house].pop();
|
||||||
this.state.hoTiles[house].push(tileId);
|
this.state.hoTiles[house].push(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.turn = null;
|
this.state.turn = null;
|
||||||
|
|
||||||
if (house === this.myHouse) {
|
if (house === this.myHouse) {
|
||||||
} else {
|
} else {
|
||||||
const canRon = Common.getHoraSets(this.myHandTiles.concat(tileId).map(id => $type(id))).length > 0;
|
const canRon = Common.getHoraSets(this.myHandTiles.concat(tid).map(id => $type(id))).length > 0;
|
||||||
const canPon = !this.isMeRiichi && this.myHandTileTypes.filter(t => t === $type(tileId)).length === 2;
|
const canPon = !this.isMeRiichi && this.myHandTileTypes.filter(t => t === $type(tid)).length === 2;
|
||||||
const canKan = !this.isMeRiichi && this.myHandTileTypes.filter(t => t === $type(tileId)).length === 3;
|
const canKan = !this.isMeRiichi && this.myHandTileTypes.filter(t => t === $type(tid)).length === 3;
|
||||||
const canCii = !this.isMeRiichi && house === Common.prevHouse(this.myHouse) &&
|
const canCii = !this.isMeRiichi && house === Common.prevHouse(this.myHouse) &&
|
||||||
Common.SHUNTU_PATTERNS.some(pattern =>
|
Common.SHUNTU_PATTERNS.some(pattern =>
|
||||||
pattern.includes($type(tileId)) &&
|
pattern.includes($type(tid)) &&
|
||||||
pattern.filter(t => this.myHandTileTypes.includes(t)).length >= 2);
|
pattern.filter(t => this.myHandTileTypes.includes(t)).length >= 2);
|
||||||
|
|
||||||
this.state.canRon = canRon ? { callee: house } : null;
|
this.state.canRon = canRon ? { callee: house } : null;
|
||||||
|
|
Loading…
Reference in a new issue