diff --git a/src/api/bot/core.ts b/src/api/bot/core.ts
index 0a073a3127..75564b81e8 100644
--- a/src/api/bot/core.ts
+++ b/src/api/bot/core.ts
@@ -475,8 +475,8 @@ class OthelloContext extends Context {
 		othelloAi('white', this.othello);
 		if (this.othello.getPattern('black').length === 0) {
 			this.bot.clearContext();
-			const blackCount = this.othello.board.map(row => row.filter(s => s == 'black').length).reduce((a, b) => a + b);
-			const whiteCount = this.othello.board.map(row => row.filter(s => s == 'white').length).reduce((a, b) => a + b);
+			const blackCount = this.othello.board.filter(s => s == 'black').length;
+			const whiteCount = this.othello.board.filter(s => s == 'white').length;
 			const winner = blackCount == whiteCount ? '引き分け' : blackCount > whiteCount ? '黒の勝ち' : '白の勝ち';
 			return this.othello.toString() + `\n\n~終了~\n\n黒${blackCount}、白${whiteCount}で${winner}です。`;
 		} else {
diff --git a/src/api/endpoints/othello/games.ts b/src/api/endpoints/othello/games.ts
index 62388b01d4..da85de1c1f 100644
--- a/src/api/endpoints/othello/games.ts
+++ b/src/api/endpoints/othello/games.ts
@@ -3,7 +3,7 @@ import Game, { pack } from '../../models/othello-game';
 
 module.exports = (params, user) => new Promise(async (res, rej) => {
 	// Get 'my' parameter
-	const [my = false, myErr] = $(params.my).boolean().$;
+	const [my = false, myErr] = $(params.my).optional.boolean().$;
 	if (myErr) return rej('invalid my param');
 
 	const q = my ? {
diff --git a/src/api/endpoints/othello/match.ts b/src/api/endpoints/othello/match.ts
index 65243a5571..cb094bbc65 100644
--- a/src/api/endpoints/othello/match.ts
+++ b/src/api/endpoints/othello/match.ts
@@ -34,6 +34,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 			black_user_id: parentIsBlack ? exist.parent_id : user._id,
 			white_user_id: parentIsBlack ? user._id : exist.parent_id,
 			turn_user_id: parentIsBlack ? exist.parent_id : user._id,
+			is_ended: false,
 			logs: []
 		});
 
diff --git a/src/api/models/othello-game.ts b/src/api/models/othello-game.ts
index b9fd94ebc0..d75baf356b 100644
--- a/src/api/models/othello-game.ts
+++ b/src/api/models/othello-game.ts
@@ -11,6 +11,9 @@ export interface IGame {
 	created_at: Date;
 	black_user_id: mongo.ObjectID;
 	white_user_id: mongo.ObjectID;
+	turn_user_id: mongo.ObjectID;
+	is_ended: boolean;
+	winner_id: mongo.ObjectID;
 	logs: any[];
 }
 
@@ -40,6 +43,7 @@ export const pack = (
 	// Populate user
 	_game.black_user = await packUser(_game.black_user_id, meId);
 	_game.white_user = await packUser(_game.white_user_id, meId);
+	_game.winner = await packUser(_game.winner_id, meId);
 
 	resolve(_game);
 });
diff --git a/src/api/stream/othello-game.ts b/src/api/stream/othello-game.ts
index 17cdd3a9e7..59d964777d 100644
--- a/src/api/stream/othello-game.ts
+++ b/src/api/stream/othello-game.ts
@@ -19,51 +19,68 @@ export default function(request: websocket.request, connection: websocket.connec
 		switch (msg.type) {
 			case 'set':
 				if (msg.pos == null) return;
-				const pos = msg.pos;
-
-				const game = await Game.findOne({ _id: gameId });
-
-				const o = new Othello();
-
-				game.logs.forEach(log => {
-					o.set(log.color, log.pos);
-				});
-
-				const myColor = game.black_user_id.equals(user._id) ? 'black' : 'white';
-				const opColor = myColor == 'black' ? 'white' : 'black';
-
-				if (!o.canReverse(myColor, pos)) return;
-				o.set(myColor, pos);
-
-				let turn;
-				if (o.getPattern(opColor).length > 0) {
-					turn = myColor == 'black' ? game.white_user_id : game.black_user_id;
-				} else {
-					turn = myColor == 'black' ? game.black_user_id : game.white_user_id;
-				}
-
-				const log = {
-					at: new Date(),
-					color: myColor,
-					pos
-				};
-
-				await Game.update({
-					_id: gameId
-				}, {
-					$set: {
-						turn_user_id: turn
-					},
-					$push: {
-						logs: log
-					}
-				});
-
-				publishOthelloGameStream(gameId, 'set', {
-					color: myColor,
-					pos
-				});
+				set(msg.pos);
 				break;
 		}
 	});
+
+	async function set(pos) {
+		const game = await Game.findOne({ _id: gameId });
+
+		if (game.is_ended) return;
+
+		const o = new Othello();
+
+		game.logs.forEach(log => {
+			o.set(log.color, log.pos);
+		});
+
+		const myColor = game.black_user_id.equals(user._id) ? 'black' : 'white';
+		const opColor = myColor == 'black' ? 'white' : 'black';
+
+		if (!o.canReverse(myColor, pos)) return;
+		o.set(myColor, pos);
+
+		let turn;
+		if (o.getPattern(opColor).length > 0) {
+			turn = myColor == 'black' ? game.white_user_id : game.black_user_id;
+		} else if (o.getPattern(myColor).length > 0) {
+			turn = myColor == 'black' ? game.black_user_id : game.white_user_id;
+		} else {
+			turn = null;
+		}
+
+		const isEnded = turn === null;
+
+		let winner;
+		if (isEnded) {
+			const blackCount = o.board.filter(s => s == 'black').length;
+			const whiteCount = o.board.filter(s => s == 'white').length;
+			winner = blackCount == whiteCount ? null : blackCount > whiteCount ? game.black_user_id : game.white_user_id;
+		}
+
+		const log = {
+			at: new Date(),
+			color: myColor,
+			pos
+		};
+
+		await Game.update({
+			_id: gameId
+		}, {
+			$set: {
+				turn_user_id: turn,
+				is_ended: isEnded,
+				winner_id: winner
+			},
+			$push: {
+				logs: log
+			}
+		});
+
+		publishOthelloGameStream(gameId, 'set', {
+			color: myColor,
+			pos
+		});
+	}
 }
diff --git a/src/web/app/common/views/components/othello.game.vue b/src/web/app/common/views/components/othello.game.vue
index cb006a289b..b7c23e704e 100644
--- a/src/web/app/common/views/components/othello.game.vue
+++ b/src/web/app/common/views/components/othello.game.vue
@@ -1,10 +1,15 @@
 <template>
 <div class="root">
 	<header><b>{{ game.black_user.name }}</b>(黒) vs <b>{{ game.white_user.name }}</b>(白)</header>
-	<p class="turn">{{ turn ? 'あなたのターンです' : '相手のターンです' }}<mk-ellipsis v-if="!turn"/></p>
+	<p class="turn" v-if="!iAmPlayer && !isEnded">{{ turn.name }}のターンです<mk-ellipsis/></p>
+	<p class="turn" v-if="iAmPlayer && !isEnded">{{ isMyTurn ? 'あなたのターンです' : '相手のターンです' }}<mk-ellipsis v-if="!isMyTurn"/></p>
+	<p class="result" v-if="isEnded">
+		<template v-if="winner"><b>{{ winner.name }}</b>の勝ち</template>
+		<template v-else>引き分け</template>
+	</p>
 	<div>
 		<div v-for="(stone, i) in o.board"
-			:class="{ empty: stone == null, myTurn: turn, can: o.canReverse(turn ? myColor : opColor, i) }"
+			:class="{ empty: stone == null, myTurn: isMyTurn, can: o.canReverse(turn.id == game.black_user.id ? 'black' : 'white', i) }"
 			@click="set(i)"
 		>
 			<img v-if="stone == 'black'" :src="`${game.black_user.avatar_url}?thumbnail&size=64`" alt="">
@@ -25,10 +30,16 @@ export default Vue.extend({
 		return {
 			o: new Othello(),
 			turn: null,
+			isMyTurn: null,
+			isEnded: false,
+			winner: null,
 			connection: null
 		};
 	},
 	computed: {
+		iAmPlayer(): boolean {
+			return this.game.black_user_id == (this as any).os.i.id || this.game.white_user_id == (this as any).os.i.id;
+		},
 		myColor(): string {
 			return this.game.black_user_id == (this as any).os.i.id ? 'black' : 'white';
 		},
@@ -41,7 +52,10 @@ export default Vue.extend({
 			this.o.set(log.color, log.pos);
 		});
 
-		this.turn = this.game.turn_user_id == (this as any).os.i.id;
+		this.turn = this.game.turn_user_id == this.game.black_user_id ? this.game.black_user : this.game.white_user;
+		this.isMyTurn = this.game.turn_user_id == (this as any).os.i.id;
+		this.isEnded = this.game.is_ended;
+		this.winner = this.game.winner;
 	},
 	mounted() {
 		this.connection = new OthelloGameStream((this as any).os.i, this.game);
@@ -53,11 +67,17 @@ export default Vue.extend({
 	},
 	methods: {
 		set(pos) {
-			if (!this.turn) return;
+			if (!this.isMyTurn) return;
 			if (!this.o.canReverse(this.myColor, pos)) return;
 			this.o.set(this.myColor, pos);
 			if (this.o.getPattern(this.opColor).length > 0) {
-				this.turn = !this.turn;
+				this.isMyTurn = !this.isMyTurn;
+				this.turn = this.myColor == 'black' ? this.game.white_user : this.game.black_user;
+			} else if (this.o.getPattern(this.myColor).length == 0) {
+				this.isEnded = true;
+				const blackCount = this.o.board.filter(s => s == 'black').length;
+				const whiteCount = this.o.board.filter(s => s == 'white').length;
+				this.winner = blackCount == whiteCount ? null : blackCount > whiteCount ? this.game.black_user : this.game.white_user;
 			}
 			this.connection.send({
 				type: 'set',
@@ -67,8 +87,28 @@ export default Vue.extend({
 		},
 		onSet(x) {
 			this.o.set(x.color, x.pos);
-			if (this.o.getPattern(this.myColor).length > 0) {
-				this.turn = true;
+			if (this.o.getPattern('black').length == 0 && this.o.getPattern('white').length == 0) {
+				this.isEnded = true;
+				const blackCount = this.o.board.filter(s => s == 'black').length;
+				const whiteCount = this.o.board.filter(s => s == 'white').length;
+				this.winner = blackCount == whiteCount ? null : blackCount > whiteCount ? this.game.black_user : this.game.white_user;
+			} else {
+				if (this.iAmPlayer && this.o.getPattern(this.myColor).length > 0) {
+					this.isMyTurn = true;
+				}
+
+				if (x.color == 'black' && this.o.getPattern('white').length > 0) {
+					this.turn = this.game.white_user;
+				}
+				if (x.color == 'black' && this.o.getPattern('white').length == 0) {
+					this.turn = this.game.black_user;
+				}
+				if (x.color == 'white' && this.o.getPattern('black').length > 0) {
+					this.turn = this.game.black_user;
+				}
+				if (x.color == 'white' && this.o.getPattern('black').length == 0) {
+					this.turn = this.game.white_user;
+				}
 			}
 			this.$forceUpdate();
 		}
diff --git a/src/web/app/common/views/components/othello.vue b/src/web/app/common/views/components/othello.vue
index f409f162f6..326a8d80cc 100644
--- a/src/web/app/common/views/components/othello.vue
+++ b/src/web/app/common/views/components/othello.vue
@@ -43,11 +43,17 @@
 				<img :src="`${g.black_user.avatar_url}?thumbnail&size=32`" alt="">
 				<img :src="`${g.white_user.avatar_url}?thumbnail&size=32`" alt="">
 				<span><b>{{ g.black_user.name }}</b> vs <b>{{ g.white_user.name }}</b></span>
-				<span class="state">{{ g.winner ? '終了' : '進行中' }}</span>
+				<span class="state">{{ g.is_ended ? '終了' : '進行中' }}</span>
 			</div>
 		</section>
-		<section>
+		<section v-if="games.length > 0">
 			<h2>みんなの対局</h2>
+			<div class="game" v-for="g in games" tabindex="-1" @click="game = g">
+				<img :src="`${g.black_user.avatar_url}?thumbnail&size=32`" alt="">
+				<img :src="`${g.white_user.avatar_url}?thumbnail&size=32`" alt="">
+				<span><b>{{ g.black_user.name }}</b> vs <b>{{ g.white_user.name }}</b></span>
+				<span class="state">{{ g.is_ended ? '終了' : '進行中' }}</span>
+			</div>
 		</section>
 	</div>
 </div>