summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2024-01-24 16:37:06 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2024-01-24 16:37:06 +0900
commit5719a929ad5a048b59cf59b8683dcaaef01f2311 (patch)
tree863fe9972e2ca7c63c06815c3f792acc755c14fc /packages
parent2024.2.0-beta.6 (diff)
downloadsharkey-5719a929ad5a048b59cf59b8683dcaaef01f2311.tar.gz
sharkey-5719a929ad5a048b59cf59b8683dcaaef01f2311.tar.bz2
sharkey-5719a929ad5a048b59cf59b8683dcaaef01f2311.zip
enhance(reversi): 変則なしマッチングを可能に
Diffstat (limited to 'packages')
-rw-r--r--packages/backend/migration/1706081514499-reversi-6.js16
-rw-r--r--packages/backend/src/core/ReversiService.ts33
-rw-r--r--packages/backend/src/core/entities/ReversiGameEntityService.ts2
-rw-r--r--packages/backend/src/models/ReversiGame.ts5
-rw-r--r--packages/backend/src/models/json-schema/reversi-game.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/reversi/match.ts5
-rw-r--r--packages/frontend/src/pages/reversi/game.setting.vue111
-rw-r--r--packages/frontend/src/pages/reversi/index.vue22
-rw-r--r--packages/misskey-js/src/autogen/apiClientJSDoc.ts4
-rw-r--r--packages/misskey-js/src/autogen/endpoint.ts4
-rw-r--r--packages/misskey-js/src/autogen/entities.ts4
-rw-r--r--packages/misskey-js/src/autogen/models.ts4
-rw-r--r--packages/misskey-js/src/autogen/types.ts8
13 files changed, 149 insertions, 77 deletions
diff --git a/packages/backend/migration/1706081514499-reversi-6.js b/packages/backend/migration/1706081514499-reversi-6.js
new file mode 100644
index 0000000000..de870be446
--- /dev/null
+++ b/packages/backend/migration/1706081514499-reversi-6.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class Reversi61706081514499 {
+ name = 'Reversi61706081514499'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "reversi_game" ADD "noIrregularRules" boolean NOT NULL DEFAULT false`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "noIrregularRules"`);
+ }
+}
diff --git a/packages/backend/src/core/ReversiService.ts b/packages/backend/src/core/ReversiService.ts
index f74416a58a..84721b2217 100644
--- a/packages/backend/src/core/ReversiService.ts
+++ b/packages/backend/src/core/ReversiService.ts
@@ -85,6 +85,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
map: game.map,
bw: game.bw,
crc32: game.crc32,
+ noIrregularRules: game.noIrregularRules,
} satisfies Partial<MiReversiGame>;
}
@@ -138,7 +139,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
}
@bindThis
- public async matchAnyUser(me: MiUser, multiple = false): Promise<MiReversiGame | null> {
+ public async matchAnyUser(me: MiUser, options: { noIrregularRules: boolean }, multiple = false): Promise<MiReversiGame | null> {
if (!multiple) {
// 既にマッチしている対局が無いか探す(3分以内)
const games = await this.reversiGamesRepository.find({
@@ -177,19 +178,29 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
2, // 自分自身のIDが入っている場合もあるので2つ取得
'REV');
- const userIds = matchings.filter(id => id !== me.id);
+ const items = matchings.filter(id => !id.startsWith(me.id));
- if (userIds.length > 0) {
- const matchedUserId = userIds[0];
+ if (items.length > 0) {
+ const [matchedUserId, option] = items[0].split(':');
- await this.redisClient.zrem('reversi:matchAny', me.id, matchedUserId);
+ await this.redisClient.zrem('reversi:matchAny',
+ me.id,
+ matchedUserId,
+ me.id + ':noIrregularRules',
+ matchedUserId + ':noIrregularRules');
- const game = await this.matched(matchedUserId, me.id);
+ const game = await this.matched(matchedUserId, me.id, {
+ noIrregularRules: options.noIrregularRules || option === 'noIrregularRules',
+ });
return game;
} else {
const redisPipeline = this.redisClient.pipeline();
- redisPipeline.zadd('reversi:matchAny', Date.now(), me.id);
+ if (options.noIrregularRules) {
+ redisPipeline.zadd('reversi:matchAny', Date.now(), me.id + ':noIrregularRules');
+ } else {
+ redisPipeline.zadd('reversi:matchAny', Date.now(), me.id);
+ }
redisPipeline.expire('reversi:matchAny', 15, 'NX');
await redisPipeline.exec();
return null;
@@ -203,7 +214,10 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
@bindThis
public async matchAnyUserCancel(user: MiUser) {
- await this.redisClient.zrem('reversi:matchAny', user.id);
+ const redisPipeline = this.redisClient.pipeline();
+ redisPipeline.zrem('reversi:matchAny', user.id);
+ redisPipeline.zrem('reversi:matchAny', user.id + ':noIrregularRules');
+ await redisPipeline.exec();
}
@bindThis
@@ -265,7 +279,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
}
@bindThis
- private async matched(parentId: MiUser['id'], childId: MiUser['id']): Promise<MiReversiGame> {
+ private async matched(parentId: MiUser['id'], childId: MiUser['id'], options: { noIrregularRules: boolean; }): Promise<MiReversiGame> {
const game = await this.reversiGamesRepository.insert({
id: this.idService.gen(),
user1Id: parentId,
@@ -278,6 +292,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
map: Reversi.maps.eighteight.data,
bw: 'random',
isLlotheo: false,
+ noIrregularRules: options.noIrregularRules,
}).then(x => this.reversiGamesRepository.findOneOrFail({
where: { id: x.identifiers[0].id },
relations: ['user1', 'user2'],
diff --git a/packages/backend/src/core/entities/ReversiGameEntityService.ts b/packages/backend/src/core/entities/ReversiGameEntityService.ts
index 6c89a70599..1a689a7b53 100644
--- a/packages/backend/src/core/entities/ReversiGameEntityService.ts
+++ b/packages/backend/src/core/entities/ReversiGameEntityService.ts
@@ -61,6 +61,7 @@ export class ReversiGameEntityService {
canPutEverywhere: game.canPutEverywhere,
loopedBoard: game.loopedBoard,
timeLimitForEachTurn: game.timeLimitForEachTurn,
+ noIrregularRules: game.noIrregularRules,
logs: game.logs,
map: game.map,
});
@@ -105,6 +106,7 @@ export class ReversiGameEntityService {
canPutEverywhere: game.canPutEverywhere,
loopedBoard: game.loopedBoard,
timeLimitForEachTurn: game.timeLimitForEachTurn,
+ noIrregularRules: game.noIrregularRules,
});
}
diff --git a/packages/backend/src/models/ReversiGame.ts b/packages/backend/src/models/ReversiGame.ts
index 11d236e458..c03335dd63 100644
--- a/packages/backend/src/models/ReversiGame.ts
+++ b/packages/backend/src/models/ReversiGame.ts
@@ -109,6 +109,11 @@ export class MiReversiGame {
@Column('boolean', {
default: false,
})
+ public noIrregularRules: boolean;
+
+ @Column('boolean', {
+ default: false,
+ })
public isLlotheo: boolean;
@Column('boolean', {
diff --git a/packages/backend/src/models/json-schema/reversi-game.ts b/packages/backend/src/models/json-schema/reversi-game.ts
index f8a5e7451c..ff4c78eeb0 100644
--- a/packages/backend/src/models/json-schema/reversi-game.ts
+++ b/packages/backend/src/models/json-schema/reversi-game.ts
@@ -82,6 +82,10 @@ export const packedReversiGameLiteSchema = {
type: 'string',
optional: false, nullable: false,
},
+ noIrregularRules: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
isLlotheo: {
type: 'boolean',
optional: false, nullable: false,
@@ -196,6 +200,10 @@ export const packedReversiGameDetailedSchema = {
type: 'string',
optional: false, nullable: false,
},
+ noIrregularRules: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
isLlotheo: {
type: 'boolean',
optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/reversi/match.ts b/packages/backend/src/server/api/endpoints/reversi/match.ts
index 62682cfb50..f8dee21c4c 100644
--- a/packages/backend/src/server/api/endpoints/reversi/match.ts
+++ b/packages/backend/src/server/api/endpoints/reversi/match.ts
@@ -37,6 +37,7 @@ export const paramDef = {
type: 'object',
properties: {
userId: { type: 'string', format: 'misskey:id', nullable: true },
+ noIrregularRules: { type: 'boolean', default: false },
multiple: { type: 'boolean', default: false },
},
required: [],
@@ -57,7 +58,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw err;
}) : null;
- const game = target ? await this.reversiService.matchSpecificUser(me, target, ps.multiple) : await this.reversiService.matchAnyUser(me, ps.multiple);
+ const game = target
+ ? await this.reversiService.matchSpecificUser(me, target, ps.multiple)
+ : await this.reversiService.matchAnyUser(me, { noIrregularRules: ps.noIrregularRules }, ps.multiple);
if (game == null) return;
diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue
index b1e66e4fdd..0fbabfe4de 100644
--- a/packages/frontend/src/pages/reversi/game.setting.vue
+++ b/packages/frontend/src/pages/reversi/game.setting.vue
@@ -12,69 +12,74 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps" :class="{ [$style.disallowInner]: isReady }">
<div style="font-size: 1.5em; text-align: center;">{{ i18n.ts._reversi.gameSettings }}</div>
- <div class="_panel">
- <div style="display: flex; align-items: center; padding: 16px; border-bottom: solid 1px var(--divider);">
- <div>{{ mapName }}</div>
- <MkButton style="margin-left: auto;" @click="chooseMap">{{ i18n.ts._reversi.chooseBoard }}</MkButton>
- </div>
+ <template v-if="game.noIrregularRules">
+ <div>{{ i18n.ts._reversi.disallowIrregularRules }}</div>
+ </template>
+ <template v-else>
+ <div class="_panel">
+ <div style="display: flex; align-items: center; padding: 16px; border-bottom: solid 1px var(--divider);">
+ <div>{{ mapName }}</div>
+ <MkButton style="margin-left: auto;" @click="chooseMap">{{ i18n.ts._reversi.chooseBoard }}</MkButton>
+ </div>
- <div style="padding: 16px;">
- <div v-if="game.map == null"><i class="ti ti-dice"></i></div>
- <div v-else :class="$style.board" :style="{ 'grid-template-rows': `repeat(${ game.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.map[0].length }, 1fr)` }">
- <div v-for="(x, i) in game.map.join('')" :class="[$style.boardCell, { [$style.boardCellNone]: x == ' ' }]" @click="onMapCellClick(i, x)">
- <i v-if="x === 'b' || x === 'w'" style="pointer-events: none; user-select: none;" :class="x === 'b' ? 'ti ti-circle-filled' : 'ti ti-circle'"></i>
+ <div style="padding: 16px;">
+ <div v-if="game.map == null"><i class="ti ti-dice"></i></div>
+ <div v-else :class="$style.board" :style="{ 'grid-template-rows': `repeat(${ game.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.map[0].length }, 1fr)` }">
+ <div v-for="(x, i) in game.map.join('')" :class="[$style.boardCell, { [$style.boardCellNone]: x == ' ' }]" @click="onMapCellClick(i, x)">
+ <i v-if="x === 'b' || x === 'w'" style="pointer-events: none; user-select: none;" :class="x === 'b' ? 'ti ti-circle-filled' : 'ti ti-circle'"></i>
+ </div>
</div>
</div>
</div>
- </div>
- <MkFolder :defaultOpen="true">
- <template #label>{{ i18n.ts._reversi.blackOrWhite }}</template>
+ <MkFolder :defaultOpen="true">
+ <template #label>{{ i18n.ts._reversi.blackOrWhite }}</template>
- <MkRadios v-model="game.bw">
- <option value="random">{{ i18n.ts.random }}</option>
- <option :value="'1'">
- <I18n :src="i18n.ts._reversi.blackIs" tag="span">
- <template #name>
- <b><MkUserName :user="game.user1"/></b>
- </template>
- </I18n>
- </option>
- <option :value="'2'">
- <I18n :src="i18n.ts._reversi.blackIs" tag="span">
- <template #name>
- <b><MkUserName :user="game.user2"/></b>
- </template>
- </I18n>
- </option>
- </MkRadios>
- </MkFolder>
+ <MkRadios v-model="game.bw">
+ <option value="random">{{ i18n.ts.random }}</option>
+ <option :value="'1'">
+ <I18n :src="i18n.ts._reversi.blackIs" tag="span">
+ <template #name>
+ <b><MkUserName :user="game.user1"/></b>
+ </template>
+ </I18n>
+ </option>
+ <option :value="'2'">
+ <I18n :src="i18n.ts._reversi.blackIs" tag="span">
+ <template #name>
+ <b><MkUserName :user="game.user2"/></b>
+ </template>
+ </I18n>
+ </option>
+ </MkRadios>
+ </MkFolder>
- <MkFolder :defaultOpen="true">
- <template #label>{{ i18n.ts._reversi.timeLimitForEachTurn }}</template>
- <template #suffix>{{ game.timeLimitForEachTurn }}{{ i18n.ts._time.second }}</template>
+ <MkFolder :defaultOpen="true">
+ <template #label>{{ i18n.ts._reversi.timeLimitForEachTurn }}</template>
+ <template #suffix>{{ game.timeLimitForEachTurn }}{{ i18n.ts._time.second }}</template>
- <MkRadios v-model="game.timeLimitForEachTurn">
- <option :value="5">5{{ i18n.ts._time.second }}</option>
- <option :value="10">10{{ i18n.ts._time.second }}</option>
- <option :value="30">30{{ i18n.ts._time.second }}</option>
- <option :value="60">60{{ i18n.ts._time.second }}</option>
- <option :value="90">90{{ i18n.ts._time.second }}</option>
- <option :value="120">120{{ i18n.ts._time.second }}</option>
- <option :value="180">180{{ i18n.ts._time.second }}</option>
- <option :value="3600">3600{{ i18n.ts._time.second }}</option>
- </MkRadios>
- </MkFolder>
+ <MkRadios v-model="game.timeLimitForEachTurn">
+ <option :value="5">5{{ i18n.ts._time.second }}</option>
+ <option :value="10">10{{ i18n.ts._time.second }}</option>
+ <option :value="30">30{{ i18n.ts._time.second }}</option>
+ <option :value="60">60{{ i18n.ts._time.second }}</option>
+ <option :value="90">90{{ i18n.ts._time.second }}</option>
+ <option :value="120">120{{ i18n.ts._time.second }}</option>
+ <option :value="180">180{{ i18n.ts._time.second }}</option>
+ <option :value="3600">3600{{ i18n.ts._time.second }}</option>
+ </MkRadios>
+ </MkFolder>
- <MkFolder :defaultOpen="true">
- <template #label>{{ i18n.ts._reversi.rules }}</template>
+ <MkFolder :defaultOpen="true">
+ <template #label>{{ i18n.ts._reversi.rules }}</template>
- <div class="_gaps_s">
- <MkSwitch v-model="game.isLlotheo" @update:modelValue="updateSettings('isLlotheo')">{{ i18n.ts._reversi.isLlotheo }}</MkSwitch>
- <MkSwitch v-model="game.loopedBoard" @update:modelValue="updateSettings('loopedBoard')">{{ i18n.ts._reversi.loopedMap }}</MkSwitch>
- <MkSwitch v-model="game.canPutEverywhere" @update:modelValue="updateSettings('canPutEverywhere')">{{ i18n.ts._reversi.canPutEverywhere }}</MkSwitch>
- </div>
- </MkFolder>
+ <div class="_gaps_s">
+ <MkSwitch v-model="game.isLlotheo" @update:modelValue="updateSettings('isLlotheo')">{{ i18n.ts._reversi.isLlotheo }}</MkSwitch>
+ <MkSwitch v-model="game.loopedBoard" @update:modelValue="updateSettings('loopedBoard')">{{ i18n.ts._reversi.loopedMap }}</MkSwitch>
+ <MkSwitch v-model="game.canPutEverywhere" @update:modelValue="updateSettings('canPutEverywhere')">{{ i18n.ts._reversi.canPutEverywhere }}</MkSwitch>
+ </div>
+ </MkFolder>
+ </template>
</div>
</div>
</MkSpacer>
diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue
index 7c687e813c..8deaead698 100644
--- a/packages/frontend/src/pages/reversi/index.vue
+++ b/packages/frontend/src/pages/reversi/index.vue
@@ -157,6 +157,7 @@ if ($i) {
const invitations = ref<Misskey.entities.UserLite[]>([]);
const matchingUser = ref<Misskey.entities.UserLite | null>(null);
const matchingAny = ref<boolean>(false);
+const noIrregularRules = ref<boolean>(false);
function startGame(game: Misskey.entities.ReversiGameDetailed) {
matchingUser.value = null;
@@ -182,6 +183,7 @@ async function matchHeatbeat() {
} else if (matchingAny.value) {
const res = await misskeyApi('reversi/match', {
userId: null,
+ noIrregularRules: noIrregularRules.value,
});
if (res != null) {
@@ -199,10 +201,22 @@ async function matchUser() {
matchHeatbeat();
}
-async function matchAny() {
- matchingAny.value = true;
-
- matchHeatbeat();
+function matchAny(ev: MouseEvent) {
+ os.popupMenu([{
+ text: i18n.ts._reversi.allowIrregularRules,
+ action: () => {
+ noIrregularRules.value = false;
+ matchingAny.value = true;
+ matchHeatbeat();
+ },
+ }, {
+ text: i18n.ts._reversi.disallowIrregularRules,
+ action: () => {
+ noIrregularRules.value = true;
+ matchingAny.value = true;
+ matchHeatbeat();
+ },
+ }], ev.currentTarget ?? ev.target);
}
function cancelMatching() {
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 4fa42bb919..c97b95e536 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -1,6 +1,6 @@
/*
- * version: 2024.2.0-beta.4
- * generatedAt: 2024-01-24T01:14:40.901Z
+ * version: 2024.2.0-beta.6
+ * generatedAt: 2024-01-24T07:32:10.455Z
*/
import type { SwitchCaseResponseType } from '../api.js';
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 6b330c7c11..e356de3453 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
/*
- * version: 2024.2.0-beta.4
- * generatedAt: 2024-01-24T01:14:40.899Z
+ * version: 2024.2.0-beta.6
+ * generatedAt: 2024-01-24T07:32:10.453Z
*/
import type {
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 8eb284ae9e..bfe40dc947 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
/*
- * version: 2024.2.0-beta.4
- * generatedAt: 2024-01-24T01:14:40.897Z
+ * version: 2024.2.0-beta.6
+ * generatedAt: 2024-01-24T07:32:10.452Z
*/
import { operations } from './types.js';
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 0a6f534aff..b7dcbfd951 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
/*
- * version: 2024.2.0-beta.4
- * generatedAt: 2024-01-24T01:14:40.896Z
+ * version: 2024.2.0-beta.6
+ * generatedAt: 2024-01-24T07:32:10.450Z
*/
import { components } from './types.js';
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 44421240d0..b5eca12a19 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -2,8 +2,8 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */
/*
- * version: 2024.2.0-beta.4
- * generatedAt: 2024-01-24T01:14:40.815Z
+ * version: 2024.2.0-beta.6
+ * generatedAt: 2024-01-24T07:32:10.370Z
*/
/**
@@ -4493,6 +4493,7 @@ export type components = {
timeoutUserId: string | null;
black: number | null;
bw: string;
+ noIrregularRules: boolean;
isLlotheo: boolean;
canPutEverywhere: boolean;
loopedBoard: boolean;
@@ -4528,6 +4529,7 @@ export type components = {
timeoutUserId: string | null;
black: number | null;
bw: string;
+ noIrregularRules: boolean;
isLlotheo: boolean;
canPutEverywhere: boolean;
loopedBoard: boolean;
@@ -25800,6 +25802,8 @@ export type operations = {
/** Format: misskey:id */
userId?: string | null;
/** @default false */
+ noIrregularRules?: boolean;
+ /** @default false */
multiple?: boolean;
};
};