diff --git a/locales/index.d.ts b/locales/index.d.ts
index 63d13f61db473ed0d0998ec87133127218510694..966c2224fb642d85bc0cf9432f12034e54fdb7bb 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -9595,6 +9595,14 @@ export interface Locale extends ILocale {
          * 相手が設定を変更しました
          */
         "opponentHasSettingsChanged": string;
+        /**
+         * 変則許可 (完全フリー)
+         */
+        "allowIrregularRules": string;
+        /**
+         * 変則なし
+         */
+        "disallowIrregularRules": string;
     };
     "_offlineScreen": {
         /**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 9f17de423272b8a7153d8c018c7b4f9e76d5331d..b9e7ea492267bbbb1ffe105edb1abcda52e4c7d4 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2556,6 +2556,8 @@ _reversi:
   shareToTlTheGameWhenStart: "開始時に対局をタイムラインに投稿"
   iStartedAGame: "対局を開始しました! #MisskeyReversi"
   opponentHasSettingsChanged: "相手が設定を変更しました"
+  allowIrregularRules: "変則許可 (完全フリー)"
+  disallowIrregularRules: "変則なし"
 
 _offlineScreen:
   title: "オフライン - サーバーに接続できません"
diff --git a/packages/backend/migration/1706081514499-reversi-6.js b/packages/backend/migration/1706081514499-reversi-6.js
new file mode 100644
index 0000000000000000000000000000000000000000..de870be446963c07ab45bb76d7a5d830983a52c0
--- /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 f74416a58a8e95dc656a61fbf97e793937b5c26f..84721b2217bbef8cc35bbd13f11a11ce7399088f 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 6c89a705991ef40fa43c0bba7b84e746a22c2fe3..1a689a7b5395a63a7ca11184fccfca89821f2650 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 11d236e4588a8358af9eb478a44578b8c24d9704..c03335dd63056831d11613d18cdea898771bb622 100644
--- a/packages/backend/src/models/ReversiGame.ts
+++ b/packages/backend/src/models/ReversiGame.ts
@@ -106,6 +106,11 @@ export class MiReversiGame {
 	})
 	public bw: string;
 
+	@Column('boolean', {
+		default: false,
+	})
+	public noIrregularRules: boolean;
+
 	@Column('boolean', {
 		default: false,
 	})
diff --git a/packages/backend/src/models/json-schema/reversi-game.ts b/packages/backend/src/models/json-schema/reversi-game.ts
index f8a5e7451c01c8f7dda7044dfb8d201c21e706ef..ff4c78eeb000ea7b4ab2433b0f540927e5cb05c2 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 62682cfb50c088ec70c7d604b0ee01a9f25bdd43..f8dee21c4ce34892f634ef5e441be44a29d0a6ee 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 b1e66e4fdd4521ffd602277c5a4633819890c08b..0fbabfe4defb2ff70eda5846caefb710e3c5ca86 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>
-
-					<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>
-
-					<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>
-
-					<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>
+					<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>
+
+					<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>
+
+					<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>
+				</template>
 			</div>
 		</div>
 	</MkSpacer>
diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue
index 7c687e813c10860bb30999120d1a3c18d97a1fdb..8deaead698bb825ea1e09aa0c2212f98280f5050 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 4fa42bb91956e26e5faa746c40eed88243645d0b..c97b95e536c182650341bcac819de0750236a60f 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 6b330c7c116d038c976e595cbc016045f96aae42..e356de345314729a6882a729a4d47cb2bf1bf205 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 8eb284ae9ec24ee91b03038d5943f315e3a93711..bfe40dc947faf66e568c5f62b0b86ff864bbee09 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 0a6f534aff3403b2766fd4716492a67b126e413b..b7dcbfd951cbc2fb095cdba032c63884c591aaa1 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 44421240d0dacb5ee9380dd2433139cab970234d..b5eca12a19d26bd73f23ad1b3e4c90075db4230f 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;
         };
       };