Skip to content

Commit 7784c93

Browse files
authored
Merge pull request #125 from boostcamp-2020/develop
3주차 코드 프리징
2 parents 8462681 + 9cd42ce commit 7784c93

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1884
-178
lines changed

.vscode/settings.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
{
22
"eslint.format.enable": true,
33
"editor.codeActionsOnSave": {
4-
"source.fixAll.eslint": true
4+
"source.fixAll.eslint": true,
5+
"source.fixAll.stylelint": true,
56
},
7+
"stylelint.enable": true,
8+
"css.validate": false,
9+
"less.validate": false,
10+
"scss.validate": false,
611
}

README.md

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1-
# 웹 기반 스토리텔링 보드게임 DUXIT 🃏
1+
# DUXIT : 웹 기반 스토리텔링 보드게임 🃏
22

33
<p align="center">
44
<img src="https://i.imgur.com/RNtI3ZD.jpg" width=500>
55
</p>
66

7-
[![GitHub stars](https://img.shields.io/github/stars/boostcamp-2020/Project18-B-Web-Duxit.svg?style=social&label=Star)](https://github.com/boostcamp-2020/Project18-B-Web-Duxit) *DUXIT은 여러분의 ⭐️ 스타⭐️ 를 먹고 삽니다.*
7+
<p align="center">
8+
<img src="https://img.shields.io/github/stars/boostcamp-2020/Project18-B-Web-Duxit.svg?style=flat&label=Star">
9+
<img src="https://github.com/boostcamp-2020/Project18-B-Web-Duxit/workflows/Deploy%20master%20branch/badge.svg?style=flat&branch=master"><br>
10+
<span style="font-weight:600">DUXIT은 여러분의 ⭐️ 스타⭐️ 를 먹고 삽니다.</span>
11+
</p>
812

913

1014
## 공개 주소 ✨
1115

1216
데모를 보고 싶다면? [DuXit.ga](http://duxit.ga/)
1317

18+
## DUXIT은 어떤 게임인가요? 🐥
19+
- DUXIT은 스토리텔링 보드게임 DIXIT을 웹으로 옮긴 온라인 게임입니다.
20+
- 번갈아가면서 각 플레이어는 스토리텔러가 되고, 자신이 가지고 있는 6장의 카드 중 하나를 고릅니다. 고른 카드에 대한 주제도 함께 정합니다.
21+
- 스토리텔러를 제외한 플레이어들은 자신이 갖고 있는 6장의 카드 중 주제와 가장 유사한 카드 한 장을 고릅니다. 모든 플레이어가 선택한 카드가 한번에 공개되면, 스토리텔러를 제외한 플레이어는 `스토리텔러가 어떤 카드를 냈을까?`를 추측하며 한 장의 카드를 고릅니다.
22+
- 이후 각 플레이어가 선택한 카드의 주인이 공개되며 정해진 점수산출 방식에 의해 말을 움직입니다. 가장 먼저 결승점에 도달하거나, 카드가 소진되었을 때 가장 멀리 말을 움직인 사람이 승리하는 게임입니다.
23+
- DUXIT은 최소 3인, 최대 6인까지 참여 가능합니다.
24+
1425
## Team. YAHTZEE 🎲
1526

1627
> [WIKI link!](https://github.com/boostcamp-2020/Project18-B-Web-Duxit/wiki)
@@ -26,16 +37,10 @@
2637

2738
| division | stack |
2839
| --------------- | --------------------------------- |
29-
| Web | babel, webpack |
30-
| Front-end | Vanilla JS, TypeScript, socket-io, Scss |
31-
| Back-end | Node.js, winston , socket-io |
32-
| Production | forever, nginx, NCP |
33-
| Code Management | Git, GitHub |
34-
| Formatting | eslint, stylelint, prettier |
40+
| Web | ![Babel](https://img.shields.io/badge/babel-v7.12.3-yellow?logo=babel) ![Webpack](https://img.shields.io/badge/webpack-v5.6.0-skyblue?logo=webpack) |
41+
| Front-end | ![Javascript](https://img.shields.io/badge/javascript-ES6-yellow?logo=javascript) ![TypeScript](https://img.shields.io/badge/TypeScript-v4.1.2-blue?logo=TypeScript) ![Sass](https://img.shields.io/badge/Sass-v1.29.0-pink?logo=Sass) ![socket.io](https://img.shields.io/badge/socket.io-v3.0.3-white?logo=socket.io) |
42+
| Back-end | ![NodeJS](https://img.shields.io/badge/node.js-v12.18-green?logo=node.js) ![Express](https://img.shields.io/badge/Express-v4.16.1-9cf?logo=express) ![winston](https://img.shields.io/badge/winston-v3.3.3-beige?logo=winston) ![socket.io](https://img.shields.io/badge/socket.io-v3.0.3-white?logo=socket.io) |
43+
| Production | ![Forever](https://img.shields.io/badge/forever-v3.0.4-blue?logo=forever) ![NginX](https://img.shields.io/badge/NginX-v1.18.0-green?logo=NginX) ![Naver Cloud Platform](https://img.shields.io/badge/Naver_Cloud_Platform-compact_server-9cf&color=brightgreen) |
44+
| Code Management | ![Git](https://img.shields.io/badge/Git-v2.27.0-red?logo=Git) ![github](https://img.shields.io/badge/github-gray?logo=github) |
45+
| Formatting | ![eslint](https://img.shields.io/badge/eslint-v7.2.0-purple?logo=eslint) ![stylelint](https://img.shields.io/badge/stylelint-v13.8.0-navy?logo=stylelint) ![prettier](https://img.shields.io/badge/prettier-v2.1.2-yellow?logo=prettier) |
3546

36-
## DUXIT은 어떤 게임인가요? 🐥
37-
- DUXIT은 스토리텔링 보드게임 DIXIT을 웹으로 옮긴 온라인 게임입니다.
38-
- 번갈아가면서 각 플레이어는 스토리텔러가 되고, 자신이 가지고 있는 6장의 카드 중 하나를 고릅니다. 고른 카드에 대한 주제도 함께 정합니다.
39-
- 스토리텔러를 제외한 플레이어들은 자신이 갖고 있는 6장의 카드 중 주제와 가장 유사한 카드 한 장을 고릅니다. 모든 플레이어가 선택한 카드가 한번에 공개되면, 스토리텔러를 제외한 플레이어는 `스토리텔러가 어떤 카드를 냈을까?`를 추측하며 한 장의 카드를 고릅니다.
40-
- 이후 각 플레이어가 선택한 카드의 주인이 공개되며 정해진 점수산출 방식에 의해 말을 움직입니다. 가장 먼저 결승점에 도달하거나, 카드가 소진되었을 때 가장 멀리 말을 움직인 사람이 승리하는 게임입니다.
41-
- DUXIT은 최소 3인, 최대 6인까지 참여 가능합니다.

src/backend/.env.sample

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
[CORS]
2-
FRONTEND_ORIGIN=
2+
FRONTEND_ORIGIN=
3+
4+
[ASSETS]
5+
CARD_COUNT=

src/backend/game/Card.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// import { randomize } from '@utils/generateRandom';
2+
3+
// const parseImageFilename = (id) => `${id.toFixed().padStart(3, 0)}.png`;
4+
5+
// const Card = class {
6+
// constructor(id) {
7+
// this.id = id;
8+
// this.src = `${process.env.CARD_ORIGIN}/${parseImageFilename(id)}`;
9+
// }
10+
// };
11+
12+
// const FULL_CARD_LIST = Array.from(
13+
// { length: process.env.CARD_COUNT },
14+
// (value, index) => new Card(index),
15+
// );
16+
// export const getRandomCards = (count) =>
17+
// randomize(FULL_CARD_LIST).slice(0, count);
18+
19+
// export default Card;

src/backend/game/Game.js

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,48 @@
11
import generateRandom from '@utils/generateRandom';
2+
import GAME_STATE from '@utils/gameState';
3+
import { PLAYER, CARD } from '@utils/number';
24
import GameList from '@game/GameList';
5+
import socketIO from '@socket';
36
import User from './User';
47

5-
const MAX_PLAYER = 6;
6-
78
export default class Game {
89
constructor(roomID) {
910
this.roomID = roomID;
1011
this.users = new Map();
1112
this.status = {
12-
isPlaying: false,
13+
state: GAME_STATE.WAITING,
1314
unusedCards: [],
1415
topic: '',
1516
turn: 0,
1617
};
1718
}
1819

20+
start() {
21+
this.status = {
22+
...this.status,
23+
state: GAME_STATE.TELLER,
24+
unusedCards: generateRandom.cards(CARD.DECK),
25+
};
26+
[...this.users.values()].forEach((user, index) => {
27+
user.initOnStart({ turnID: index });
28+
});
29+
this.startNewRound();
30+
}
31+
32+
end() {
33+
// 아직 사용되지 않은 함수
34+
this.status = {
35+
...this.status,
36+
state: GAME_STATE.WAITING,
37+
};
38+
}
39+
1940
isEnterable() {
20-
if (this.status.isPlaying || this.users.size >= MAX_PLAYER) return false;
41+
if (
42+
this.status.state !== GAME_STATE.WAITING ||
43+
this.users.size >= PLAYER.MAX
44+
)
45+
return false;
2146
return true;
2247
}
2348

@@ -39,11 +64,75 @@ export default class Game {
3964
}
4065

4166
getUser(socketID) {
42-
if (!this.users.has(socketID)) {
43-
console.log(`getUser: socketID ${socketID} does not exist`);
44-
return null;
45-
}
46-
67+
if (!this.users.has(socketID)) return null;
4768
return this.users.get(socketID);
4869
}
70+
71+
getUsersProfile() {
72+
return [...this.users.keys()].map((socketID) => {
73+
return { ...this.users.get(socketID).getProfile(), socketID };
74+
});
75+
}
76+
77+
updateUserProfile({ socketID, nickname, color }) {
78+
const user = this.users.get(socketID);
79+
user.setColor(color);
80+
user.setNickname(nickname);
81+
}
82+
83+
dealCards(cards, count) {
84+
const newCards = this.status.unusedCards.slice(0, count);
85+
this.status = {
86+
...this.status,
87+
unusedCards: [...this.status.unusedCards.slice(count)],
88+
};
89+
return [...cards, ...newCards];
90+
}
91+
92+
getUserArray() {
93+
return [...this.users.values()];
94+
}
95+
96+
forceGuesserSelect() {
97+
this.getUserArray()
98+
.filter((user) => user.submittedCard === null)
99+
.forEach((user) => {
100+
user.submittedCard = generateRandom.pickOneFromArray(user.cards);
101+
socketIO
102+
.to(user.socketID)
103+
.emit('guesser select card', { cardID: user.submittedCard });
104+
});
105+
}
106+
107+
startNewRound() {
108+
// Initialize Game status
109+
this.status = {
110+
...this.status,
111+
state: GAME_STATE.TELLER,
112+
topic: '',
113+
turn: this.status.turn + 1,
114+
};
115+
const {
116+
users,
117+
status: { unusedCards, turn },
118+
} = this;
119+
const isFirstTurn = turn === 1;
120+
const teller = [...users.values()][turn % users.size];
121+
const { socketID: tellerID } = teller;
122+
const requiredCardCount = isFirstTurn ? CARD.HAND : 1;
123+
124+
// 카드가 부족한지 체크
125+
const outOfDeck = unusedCards.length < users.size * requiredCardCount;
126+
// if (outOfDeck) {
127+
// // TODO: 점수나 승자같은 결과를 내면서 턴을 끝내야되요~
128+
// return;
129+
// }
130+
131+
users.forEach((user) => {
132+
const cards = this.dealCards(user.cards, requiredCardCount);
133+
const params = { tellerID, cards };
134+
user.initOnRound(params);
135+
socketIO.to(user.socketID).emit('get round data', params);
136+
});
137+
}
49138
}

src/backend/game/GameList.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ class GameList {
2626
getGame(ID) {
2727
const game = this.games.get(ID);
2828
if (!game) {
29-
console.log(`Game.findGame: can not find ${ID}`);
3029
return null;
3130
}
3231

src/backend/game/User.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,35 @@ class User {
66
this.turnID = 0;
77
this.submittedCard = null;
88
this.votedCard = null;
9-
this.isTeller = null;
9+
this.isTeller = false;
1010
this.cards = [];
1111
this.score = 0;
12+
this.isReady = false;
13+
}
14+
15+
initOnStart({ turnID } = {}) {
16+
this.turnID = turnID;
17+
this.score = 0;
18+
}
19+
20+
initOnRound({ tellerID = '', cards = [] } = {}) {
21+
this.submittedCard = null;
22+
this.votedCard = null;
23+
this.isReady = false;
24+
this.isTeller = this.socketID === tellerID;
25+
this.cards = cards;
26+
}
27+
28+
setReady(isReady) {
29+
this.isReady = isReady;
30+
}
31+
32+
setColor(color) {
33+
this.color = color;
34+
}
35+
36+
setNickname(nickname) {
37+
this.nickname = nickname;
1238
}
1339

1440
submitCard(cardID) {
@@ -23,7 +49,7 @@ class User {
2349
this.cards = [...this.cards, cardID];
2450
}
2551

26-
getUserInfo() {
52+
getState() {
2753
const {
2854
socketID,
2955
nickname,
@@ -34,6 +60,7 @@ class User {
3460
isTeller,
3561
cards,
3662
score,
63+
isReady,
3764
} = this;
3865

3966
return {
@@ -46,14 +73,16 @@ class User {
4673
isTeller,
4774
cards,
4875
score,
76+
isReady,
4977
};
5078
}
5179

5280
getProfile() {
53-
const { nickname, color } = this;
81+
const { nickname, color, score } = this;
5482
return {
5583
nickname,
5684
color,
85+
score,
5786
};
5887
}
5988
}

src/backend/package-lock.json

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/backend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
"version": "0.0.0",
44
"private": true,
55
"scripts": {
6-
"start": "npx webpack --watch",
6+
"start": "npx webpack --watch --mode=development",
77
"dev": "npm start",
88
"build": "npx webpack --mode=production"
99
},
1010
"dependencies": {
1111
"cors": "^2.8.5",
1212
"debug": "~2.6.9",
13-
"dotenv": "^8.2.0",
1413
"express": "~4.16.1",
1514
"morgan": "~1.9.1",
1615
"socket.io": "^3.0.3",
@@ -24,6 +23,7 @@
2423
"@babel/preset-env": "^7.12.1",
2524
"babel-loader": "^8.2.1",
2625
"core-js": "^3.7.0",
26+
"dotenv-webpack": "^6.0.0",
2727
"eslint": "^7.2.0",
2828
"eslint-config-airbnb-base": "^14.2.1",
2929
"eslint-config-prettier": "^6.15.0",

src/backend/server.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@
22
* Module dependencies.
33
*/
44

5-
import dotenv from 'dotenv';
65
import debugModule from 'debug';
76
import http from 'http';
87
import socketIO from './sockets';
98
import createApplication from './app';
109

1110
const debug = debugModule('backend:server');
1211

13-
// dotenv
14-
dotenv.config();
15-
1612
/**
1713
* Normalize a port into a number, string, or false.
1814
*/

src/backend/sockets/chat.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import logger from '@utils/winston';
2+
13
function onSendChat({ message }) {
24
const socket = this;
35
// game = socket.game
46
const { game } = socket;
57
const { nickname } = game.getUser(socket.id).getProfile();
8+
logger.info(`chat ip:${socket.handshake.address} msg:${message}`);
69
socket.in(game.roomID).emit('send chat', { message, nickname });
710
}
811

0 commit comments

Comments
 (0)