Compare commits
116 commits
game_logic
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
08745443ca | ||
|
177d3a4c42 | ||
|
1d804694b7 | ||
|
a40253a4fd | ||
|
1c470ffec0 | ||
|
27cf281670 | ||
|
0fad98a1d0 | ||
|
14a94be59e | ||
|
0f3e7942ef | ||
|
cfec49845b | ||
|
2a84644e12 | ||
|
e1a6cd9a44 | ||
|
a3420c1152 | ||
|
c4192f2e33 | ||
|
cbf9d461ba | ||
|
5eca128110 | ||
|
dd0289439c | ||
|
9cae5ff185 | ||
|
0ec92dd5e9 | ||
|
ff3d6c2c24 | ||
|
1e6050a486 | ||
|
2f1174c21b | ||
|
ccbeddfa40 | ||
|
f8779ae887 | ||
|
a9be49c38e | ||
|
5e33e6439e | ||
|
ea951bee66 | ||
|
d7c53e7c7f | ||
|
a290ec532d | ||
|
9ed4fc0a39 | ||
|
62fe6dfcb4 | ||
|
3955f940e2 | ||
|
f14c0ce087 | ||
|
5eedd6b2e9 | ||
|
d803f55f5b | ||
|
35c51c567f | ||
|
8414d13949 | ||
|
aed378c021 | ||
|
4b507cce96 | ||
|
a8119bb194 | ||
|
692a8b1400 | ||
|
52762475f7 | ||
|
0db13d2913 | ||
|
b1d43b5b02 | ||
|
e40fa63274 | ||
|
a3aecfeadc | ||
|
0b2bfad12f | ||
|
72b93f8ff1 | ||
|
18027ec1ef | ||
|
be8cb70431 | ||
|
249789944b | ||
|
17b2e2c31f | ||
|
77ced54c6f | ||
|
97242a2235 | ||
|
f010d5d9ee | ||
|
6359089e91 | ||
|
49873aa706 | ||
|
12f9847a0e | ||
|
09b346064f | ||
|
84085dc8b2 | ||
|
934b1b7b2d | ||
|
7c2bf6416b | ||
|
d1f3459516 | ||
|
1b3dbd870a | ||
|
19a5282b73 | ||
|
55a282b5c8 | ||
|
796bb7aad9 | ||
|
8fa6e207ed | ||
|
d54d43c42c | ||
|
181554124b | ||
|
3eabf4191a | ||
|
9b419a416b | ||
|
e41d889177 | ||
|
2922be86e1 | ||
|
7eb592a397 | ||
|
572a1dcf19 | ||
|
ef0e53756e | ||
|
9f18c01839 | ||
|
8cdefa98c1 | ||
|
475db4e812 | ||
|
73ba2bb237 | ||
|
a324ece0af | ||
|
2e613daa1b | ||
|
8e25106be6 | ||
|
c43df2cd5f | ||
|
72d31cca5b | ||
|
54fd676656 | ||
|
155cca9110 | ||
|
7aed5b7bf9 | ||
|
a02576532d | ||
|
b8eb3770d9 | ||
|
a99426e03f | ||
|
d51e3f72f4 | ||
|
bdeb9c9d86 | ||
|
88e51fdbf9 | ||
|
2237e344c1 | ||
|
5ad997d276 | ||
|
9646656f7a | ||
|
1142c1d448 | ||
|
e9d94d1fad | ||
|
1a1e5121d7 | ||
|
a5fbeea929 | ||
|
13a882d212 | ||
|
96af52823d | ||
|
9164ee5987 | ||
|
4a90b933a7 | ||
|
089783c82d | ||
|
e553601af7 | ||
|
b1f29f3d2d | ||
|
bdfb6ebe85 | ||
|
a54d4bf7e4 | ||
|
37db281d08 | ||
|
41e6e662e9 | ||
|
8fb1b80cb6 | ||
|
e53a8f4f2a | ||
|
7408e409aa |
60 changed files with 30437 additions and 24339 deletions
|
@ -43,6 +43,7 @@ jobs:
|
||||||
- node_modules
|
- node_modules
|
||||||
key:
|
key:
|
||||||
v1-dependencies-{{ checksum "package-lock.json" }}
|
v1-dependencies-{{ checksum "package-lock.json" }}
|
||||||
|
- run: npm test
|
||||||
- run: npm install react
|
- run: npm install react
|
||||||
- run: npm install react-dom
|
- run: npm install react-dom
|
||||||
|
|
||||||
|
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Sorrel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
83
README.md
83
README.md
|
@ -1,10 +1,64 @@
|
||||||
# Node Go
|
# Node Go
|
||||||
A browser application to play Go in real time.
|
A browser application to play Go in real time.
|
||||||
|
|
||||||
## Development Demo
|
|
||||||
[The project in it's current state](https://play-node-go.herokuapp.com/)
|
[The project in it's current state](https://play-node-go.herokuapp.com/)
|
||||||
[Client only prototype](https://sorrelbri.github.io/browser-go-proto/)
|
[Client only prototype](https://sorrelbri.github.io/browser-go-proto/)
|
||||||
|
|
||||||
|
![Screenshot of an in-progress game of Go.](public/game-in-progress.png)
|
||||||
|
|
||||||
|
[About Go](#the-game-of-go)
|
||||||
|
[Technical Challenges](#technical-challenges)
|
||||||
|
[Setup For Development](#setup)
|
||||||
|
[Known Bugs](#known-bugs)
|
||||||
|
[Roadmap](#roadmap)
|
||||||
|
[Features](#features)
|
||||||
|
[Tech](#built-with)
|
||||||
|
|
||||||
|
---
|
||||||
|
## The Game of Go
|
||||||
|
Go is a 2 player abstract strategy game of perfect information.
|
||||||
|
|
||||||
|
Players take turns placing playing pieces called stones on the intersections of a gridded board. This board is usually a square 19 points across. Stones remain on the points at which they are placed unless they are captured by the opposing player. Capture occurs when a stone or group of stones no longer has any adjascent empty points.
|
||||||
|
|
||||||
|
Play ends when both players agree that they have exhausted all advantageous moves. Scoring is determined by counting and comparing the area controlled by either player.
|
||||||
|
|
||||||
|
For a more detailed explanation of the rules, please see [my previous illustrated explanation of the game of go](https://github.com/sorrelbri/browser-go-proto#the-game-of-go) or the [American Go Association's Concise Rules of Go.](https://www.usgo.org/aga-concise-rules-go)
|
||||||
|
|
||||||
|
---
|
||||||
|
## Technical Challenges
|
||||||
|
### Modeling Game State
|
||||||
|
A go board typically consists of 361 points which can exist in a number of states. Points can influence the state of points that are orthogonal neighbors. This relationship can be thought of as an undirected graph, with each point being a vertex typically of degree 4. Special cases include 'edge' points, whose degree is 3 or, in the case of corner points, 2.
|
||||||
|
|
||||||
|
Many of the methods that manage the state of the game and of the board, make use of this graph representation. Groups are contiguous points with the same color stone which are important in determining the life or death of stones on the board. When a player makes a move, (provided that move is legal,) the point at which the move is made will utilize a breadth-first graph traversal calling a `joinGroup` method on each point with the same color stone.
|
||||||
|
|
||||||
|
Adjacent points without stones are very important to the state of a point as well. These are known as liberties, and so long as a group of stones has at least one point with at least one liberty, that group remains alive and on the board. Therefore, the `joinGroup` method also utilizes a depth first traversal to mark all of the liberties of a group. Both the stones and the liberties of the group are memoized on the Game object.
|
||||||
|
|
||||||
|
![Image of Game logic](public/game-logic.png)
|
||||||
|
|
||||||
|
### Game Records
|
||||||
|
For the moment, game records are modeled as a list where each move has the type `{player: <string: player color>, pos: {x: <integer>, y: <integer>}}`.
|
||||||
|
|
||||||
|
The database `move` table contains additional information `number`, `game_record`, and `prior_move` in addition to a foreign key for a `game` row. `number` represents the move number in a game record (for now this corresponds to list position), `game_record` is a boolean representing whether the move was 'official' or is an alternate move used in study, and `prior_move` is a reference to another `move` row allowing for the construction of a tree model for the game (see [Expanding this representation](#expanding-this-representation), below.)
|
||||||
|
|
||||||
|
There is a backend service that processes this list of moves into a current board state ([Modeling Game State](#modeling-game-state).) On the frontend, users have the option of expanding a menu to view the move order in the format below.
|
||||||
|
|
||||||
|
![Game Record: Black and white 19x19 grid with moves represented as circles filled in with stone color and marked with move number. Below the grid is an overflow area for moves made at previously played points in the format /[new move number and color at old move number and color/]](public/game-record.png)
|
||||||
|
|
||||||
|
This is a customary representation of game records printed in Go literature. A frontend service processes the list of moves and plots each move onto a `<canvas>` element drawn to resemble the grid lines on a board, with moves that are placed at prior points plotted on an additional `<canvas>` element below.
|
||||||
|
|
||||||
|
#### Expanding this representation
|
||||||
|
This representation will be expanded when support for alternate game paths is added. Alternate game paths will allow users to study completed games by playing out the consequences of moves not in the completed game. This feature will require a tree structure for the game record where moves on the main line are referred with a `main` property on each move node and alternate moves with any number of alternate references.
|
||||||
|
|
||||||
|
### Caching multiple in-progress games
|
||||||
|
![Image of Game module in context](public/game-module.png)
|
||||||
|
|
||||||
|
### Partitioning Game Rooms
|
||||||
|
Finding a game starts with joining a game room.
|
||||||
|
![Image of Home Screen with 'main' game room](public/home-screen.png)
|
||||||
|
Watch an in progress game, join a game, or study a historic game.
|
||||||
|
![Image of Room Screen with multiple games in various states](public/room-screen.png)
|
||||||
|
|
||||||
|
---
|
||||||
## Setup
|
## Setup
|
||||||
### Local Repo
|
### Local Repo
|
||||||
```sh
|
```sh
|
||||||
|
@ -77,23 +131,34 @@ $ cd packages/play-node-go
|
||||||
$ npm start
|
$ npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
## Known Bugs
|
## Known Bugs
|
||||||
- server side move validation not complete, full game logic not implemented
|
- game end logic not implemented on front end yet
|
||||||
- no authorization for game moves
|
- no authorization for game moves
|
||||||
- websocket connections may remain open, pooling prevents runaway leaks, but tests may hang
|
- websocket connections may remain open, pooling prevents runaway leaks, but tests may hang
|
||||||
|
|
||||||
## Roadmap 4/20
|
---
|
||||||
1. Game logic module
|
## Roadmap
|
||||||
2. Game in progress caching
|
### 6/20
|
||||||
3. Auth for games
|
1. Frontend implementation of game end logic
|
||||||
4. Game request creation
|
2. Auth for games
|
||||||
|
3. Game request creation
|
||||||
|
### 7/20
|
||||||
|
1. Generate game records
|
||||||
|
2. Implement chat
|
||||||
|
3. Implement study mode
|
||||||
|
|
||||||
|
---
|
||||||
## Features
|
## Features
|
||||||
- [ ] Realtime communications
|
- [x] Realtime play
|
||||||
|
- [x] Account authentication
|
||||||
|
- [ ] Chat
|
||||||
|
- [ ] Study mode
|
||||||
- [ ] Multiple game settings
|
- [ ] Multiple game settings
|
||||||
- [ ] Customizable board size
|
- [ ] Customizable board size
|
||||||
|
- [ ] Download games in .sgf format
|
||||||
|
|
||||||
|
---
|
||||||
## Built with
|
## Built with
|
||||||
- [Express](https://expressjs.com)
|
- [Express](https://expressjs.com)
|
||||||
- [React](https://reactjs.org)
|
- [React](https://reactjs.org)
|
||||||
|
|
18933
package-lock.json
generated
18933
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -5,10 +5,6 @@
|
||||||
"test": "lerna run test",
|
"test": "lerna run test",
|
||||||
"bootstrap": "lerna bootstrap --hoist"
|
"bootstrap": "lerna bootstrap --hoist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"react": "^16.13.1",
|
|
||||||
"react-dom": "^16.13.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-flow": "^7.8.3",
|
"@babel/preset-flow": "^7.8.3",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
|
|
14946
packages/play-node-go/package-lock.json
generated
Normal file
14946
packages/play-node-go/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -5,12 +5,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mars/heroku-js-runtime-env": "^3.0.2",
|
"@mars/heroku-js-runtime-env": "^3.0.2",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
"@testing-library/user-event": "^7.1.2",
|
||||||
"node-sass": "^4.13.0",
|
"node-sass": "^4.14.1",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"react-dom": "^16.13.1",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
"react-scripts": "^3.4.0",
|
"react-scripts": "^3.4.0",
|
||||||
"socket.io-client": "^2.3.0",
|
"socket.io-client": "^2.3.0"
|
||||||
"react": "^16.13.1",
|
|
||||||
"react-dom": "^16.13.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "REACT_APP_ENVIRONMENT='development' react-scripts start",
|
"start": "REACT_APP_ENVIRONMENT='development' react-scripts start",
|
||||||
|
|
|
@ -51,3 +51,8 @@ table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
|
@ -36,6 +36,8 @@ function App() {
|
||||||
dispatch({type:'SOCKET', message: 'LAUNCH', body:{nsp:'', dispatch}});
|
dispatch({type:'SOCKET', message: 'LAUNCH', body:{nsp:'', dispatch}});
|
||||||
}
|
}
|
||||||
socketConnect();
|
socketConnect();
|
||||||
|
|
||||||
|
return () => dispatch({type: 'SOCKET', message: 'DISCONNECT', body: {}});
|
||||||
}, [ state.connect ])
|
}, [ state.connect ])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
34
packages/play-node-go/src/components/Button/Guest/Guest.js
Normal file
34
packages/play-node-go/src/components/Button/Guest/Guest.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import React from "react";
|
||||||
|
import authServices from "../../../services/authServices";
|
||||||
|
|
||||||
|
const Guest = ({ dispatch }) => {
|
||||||
|
const handleClick = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// dispatch to guest endpoint
|
||||||
|
const guestResponse = await authServices.guestService();
|
||||||
|
|
||||||
|
if (guestResponse.errors) {
|
||||||
|
const authError = guestResponse.errors[0].auth;
|
||||||
|
return dispatch({
|
||||||
|
type: "ERR",
|
||||||
|
message: "AUTH_ERROR",
|
||||||
|
body: { authError },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatch({
|
||||||
|
type: "AUTH",
|
||||||
|
message: "GUEST",
|
||||||
|
body: guestResponse,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button className="nav__section__button" onClick={handleClick}>
|
||||||
|
Continue As Guest
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Guest;
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import './ActionError.scss';
|
import './ActionError.scss';
|
||||||
|
|
||||||
const ActionError = (props) => {
|
const ActionError = (props) => {
|
||||||
const errorMessage = props.error;
|
const errorMessage = props.error || '';
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className="error error--action"
|
className="error error--action"
|
||||||
|
|
|
@ -1,45 +1,49 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import Login from '../Login/Login';
|
import Login from "../Login/Login";
|
||||||
import Signup from '../Signup/Signup';
|
import Signup from "../Signup/Signup";
|
||||||
|
import Guest from "../../Button/Guest/Guest";
|
||||||
|
|
||||||
const Auth = (props) => {
|
const Auth = (props) => {
|
||||||
const [ showForm, setShowForm ] = useState('login')
|
const [showForm, setShowForm] = useState("login");
|
||||||
const { state, dispatch } = props;
|
const { state, dispatch } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className="nav__section nav__section--auth"
|
className="nav__section nav__section--auth"
|
||||||
onClick={()=>{setShowForm('login')}}
|
onClick={() => {
|
||||||
|
setShowForm("login");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<p
|
<p className="nav__section__label">Login</p>
|
||||||
className="nav__section__label"
|
|
||||||
>Login</p>
|
|
||||||
|
|
||||||
{
|
{showForm === "login" ? (
|
||||||
showForm === 'login'
|
<Login dispatch={dispatch} state={state} />
|
||||||
? <Login dispatch={dispatch} state={state}/>
|
) : (
|
||||||
: <></>
|
<></>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="nav__section nav__section--auth"
|
className="nav__section nav__section--auth"
|
||||||
onClick={()=>{setShowForm('signup')}}
|
onClick={() => {
|
||||||
|
setShowForm("signup");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<p
|
<p className="nav__section__label">Signup</p>
|
||||||
className="nav__section__label"
|
|
||||||
>Signup</p>
|
|
||||||
|
|
||||||
{
|
{showForm === "signup" ? (
|
||||||
showForm === 'signup'
|
<Signup dispatch={dispatch} state={state} />
|
||||||
? <Signup dispatch={dispatch} state={state}/>
|
) : (
|
||||||
: <></>
|
<></>
|
||||||
}
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="nav__section nav__section--auth">
|
||||||
|
<Guest dispatch={dispatch} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Auth;
|
export default Auth;
|
||||||
|
|
|
@ -1,38 +1,74 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import './Board.scss';
|
import "./Board.scss";
|
||||||
import Point from '../Point/Point';
|
import Point from "../Point/Point";
|
||||||
|
|
||||||
const Board = (props) => {
|
const Board = (props) => {
|
||||||
const { game, user, dispatch, board } = props;
|
const { game, user, dispatch, board, meta } = props;
|
||||||
const sizeFlag = `Game__board--size-${ game.boardSize }`
|
const sizeFlag = `Game__board--size-${game.boardSize}`;
|
||||||
|
const hoshiPoints = {
|
||||||
|
9: { "3-3": true, "7-7": true, "3-7": true, "7-3": true },
|
||||||
|
13: {
|
||||||
|
"7-7": true,
|
||||||
|
"10-7": true,
|
||||||
|
"7-4": true,
|
||||||
|
"7-10": true,
|
||||||
|
"4-7": true,
|
||||||
|
"4-4": true,
|
||||||
|
"10-10": true,
|
||||||
|
"4-10": true,
|
||||||
|
"10-4": true,
|
||||||
|
},
|
||||||
|
19: {
|
||||||
|
"10-10": true,
|
||||||
|
"16-10": true,
|
||||||
|
"10-4": true,
|
||||||
|
"10-16": true,
|
||||||
|
"4-10": true,
|
||||||
|
"4-4": true,
|
||||||
|
"16-16": true,
|
||||||
|
"4-16": true,
|
||||||
|
"16-4": true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const renderPoints = boardSize => {
|
const isHoshi = (posX, posY) =>
|
||||||
let i = 0, boardPoints = [];
|
hoshiPoints[game.boardSize][`${posX}-${posY}`];
|
||||||
|
|
||||||
|
const renderPoints = (boardSize) => {
|
||||||
|
let i = 0,
|
||||||
|
boardPoints = [];
|
||||||
while (i < boardSize * boardSize) {
|
while (i < boardSize * boardSize) {
|
||||||
const posX = Math.floor(i / boardSize) + 1;
|
const posX = Math.floor(i / boardSize) + 1;
|
||||||
const posY = i % boardSize + 1;
|
const posY = (i % boardSize) + 1;
|
||||||
console.log(board[`${posX}-${posY}`])
|
const pointData = board[`${posX}-${posY}`];
|
||||||
|
const dotData =
|
||||||
|
meta && meta.turn === 0 && !meta.winner && meta.territory
|
||||||
|
? meta.territory[`${posX}-${posY}`]
|
||||||
|
: game.turn || meta?.turn;
|
||||||
boardPoints.push(
|
boardPoints.push(
|
||||||
<Point
|
<Point
|
||||||
key={`${posX}-${posY}`}
|
key={`${posX}-${posY}`}
|
||||||
posX={posX}
|
posX={posX}
|
||||||
posY={posY}
|
posY={posY}
|
||||||
pointData={board[`${posX}-${posY}`]}
|
pointData={pointData}
|
||||||
// point={board[posX][posY]}
|
dotData={dotData}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
user={user}
|
user={user}
|
||||||
|
meta={meta}
|
||||||
|
hoshi={isHoshi(posX, posY)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
); i++;
|
);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
return boardPoints;
|
return boardPoints;
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`Game__board ${sizeFlag}`}>
|
<div className={`Game__board ${sizeFlag}`}>
|
||||||
{game.id ? renderPoints(game.boardSize) : <></>}
|
{game.id ? renderPoints(game.boardSize) : <></>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Board;
|
export default Board;
|
15
packages/play-node-go/src/components/GameUI/Kifu/Kifu.js
Normal file
15
packages/play-node-go/src/components/GameUI/Kifu/Kifu.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import React from "react";
|
||||||
|
import "./Kifu.scss";
|
||||||
|
|
||||||
|
const Kifu = ({ clickKifu }) => {
|
||||||
|
return (
|
||||||
|
<div className="Kifu">
|
||||||
|
<p className="Kifu__show-menu" onClick={clickKifu}>
|
||||||
|
Show Menu?
|
||||||
|
</p>
|
||||||
|
<div className="Kifu__board"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Kifu;
|
31
packages/play-node-go/src/components/GameUI/Kifu/Kifu.scss
Normal file
31
packages/play-node-go/src/components/GameUI/Kifu/Kifu.scss
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
@import '../../../../public/stylesheets/partials/mixins';
|
||||||
|
|
||||||
|
div.Kifu {
|
||||||
|
order: 0;
|
||||||
|
height: 10vh;
|
||||||
|
width: 8vh;
|
||||||
|
background-color: #FFF;
|
||||||
|
transform: rotate(-20deg);
|
||||||
|
|
||||||
|
p.Kifu__show-menu {
|
||||||
|
display: none;
|
||||||
|
margin: 3vh auto;
|
||||||
|
position: absolute;
|
||||||
|
transform: rotate(20deg);
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
p.Kifu__show-menu {
|
||||||
|
@include gameViewLabel;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.Kifu__board {
|
||||||
|
border: 0.25vh solid #444;
|
||||||
|
width: 5vh;
|
||||||
|
height: 5vh;
|
||||||
|
margin: 1vh 2vh 2vh auto;
|
||||||
|
}
|
||||||
|
}
|
194
packages/play-node-go/src/components/GameUI/Menu/Menu.js
Normal file
194
packages/play-node-go/src/components/GameUI/Menu/Menu.js
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import "./Menu.scss";
|
||||||
|
|
||||||
|
const Menu = ({ showMenu, clickClose, ...props }) => {
|
||||||
|
const { active, meta } = props.state; // active.game.boardSize, meta.gameRecord
|
||||||
|
const boardSize = active.game.boardSize;
|
||||||
|
const handleBackgroundClick = (e) => {
|
||||||
|
if (e.target.className === "Game__Menu-container") clickClose();
|
||||||
|
};
|
||||||
|
const canvasRef = useRef();
|
||||||
|
const overflowRef = useRef();
|
||||||
|
const drawGameRecord = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<canvas ref={canvasRef} />
|
||||||
|
<canvas ref={overflowRef} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickPrint = () => {
|
||||||
|
const iframe = document.createElement("iframe");
|
||||||
|
const body = document.getElementsByTagName("body")[0];
|
||||||
|
const record = document.getElementById("record");
|
||||||
|
body.appendChild(iframe);
|
||||||
|
// move game record to iframe and print iframe
|
||||||
|
iframe.contentDocument.body.appendChild(canvasRef.current);
|
||||||
|
iframe.contentDocument.body.appendChild(overflowRef.current);
|
||||||
|
iframe.contentWindow.print();
|
||||||
|
// move game record back to menu and remove iframe
|
||||||
|
record.appendChild(canvasRef.current);
|
||||||
|
record.appendChild(overflowRef.current);
|
||||||
|
body.removeChild(iframe);
|
||||||
|
};
|
||||||
|
|
||||||
|
// draw canvases
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
const scale = Math.min(window.innerWidth * 0.75, 500);
|
||||||
|
canvas.height = scale;
|
||||||
|
canvas.width = scale;
|
||||||
|
const space = scale / boardSize;
|
||||||
|
const offset = space / 2;
|
||||||
|
for (let i = 0; i < boardSize; i++) {
|
||||||
|
const start = i * space + offset;
|
||||||
|
const end = scale - offset;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(start, offset);
|
||||||
|
ctx.lineTo(start, end);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(offset, start);
|
||||||
|
ctx.lineTo(end, start);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
if (!meta?.gameRecord) return;
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
const { overflow } = meta.gameRecord.reduce(
|
||||||
|
(dict, { player, pos }, index) => {
|
||||||
|
const past = dict[`${pos.x}-${pos.y}`];
|
||||||
|
if (past) {
|
||||||
|
// overflow: [ { move:#, player:'color', subsequentMoves: [ { move: #, player: 'color' } ] } ]
|
||||||
|
if (dict.overflow) {
|
||||||
|
const indexOfPrior = dict.overflow.findIndex(
|
||||||
|
({ move }) => move === past
|
||||||
|
);
|
||||||
|
if (indexOfPrior !== -1) {
|
||||||
|
// if multiple past moves at this point exist
|
||||||
|
dict.overflow[indexOfPrior].subsequentMoves.push({
|
||||||
|
move: index + 1,
|
||||||
|
player,
|
||||||
|
});
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
// if a second move at this point has not yet been encountered
|
||||||
|
// prior move will be black if no active handicap and move is odd or if active handicap and move is even
|
||||||
|
const playerPrior =
|
||||||
|
(active.handicap && !(past % 2)) || past % 2 ? "black" : "white";
|
||||||
|
return {
|
||||||
|
...dict,
|
||||||
|
overflow: [
|
||||||
|
...dict.overflow,
|
||||||
|
{
|
||||||
|
move: past,
|
||||||
|
player: playerPrior,
|
||||||
|
subsequentMoves: [{ move: index + 1, player }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// if no move has yet been encountered at a previously made move
|
||||||
|
return {
|
||||||
|
...dict,
|
||||||
|
overflow: [
|
||||||
|
{ move: past, subsequentMoves: [{ move: index + 1, player }] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(
|
||||||
|
(pos.y - 1) * space + offset,
|
||||||
|
(pos.x - 1) * space + offset,
|
||||||
|
offset * 0.95,
|
||||||
|
0,
|
||||||
|
Math.PI * 2,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillStyle = player === "white" ? "#fff" : "#000";
|
||||||
|
ctx.fill();
|
||||||
|
ctx.fillStyle = player === "white" ? "#000" : "#fff";
|
||||||
|
|
||||||
|
ctx.fillText(
|
||||||
|
index + 1,
|
||||||
|
(pos.y - 1) * space + offset,
|
||||||
|
(pos.x - 1) * space + offset
|
||||||
|
);
|
||||||
|
return { ...dict, [`${pos.x}-${pos.y}`]: index + 1 };
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
if (!overflow?.length) return;
|
||||||
|
// Draw Overflow Moves (moves made at prior points)
|
||||||
|
const canvas2 = overflowRef.current;
|
||||||
|
const ctx2 = canvas2.getContext("2d");
|
||||||
|
canvas2.width = scale;
|
||||||
|
canvas2.height = space * overflow.length;
|
||||||
|
ctx2.textAlign = "center";
|
||||||
|
ctx2.textBaseline = "middle";
|
||||||
|
overflow.forEach(({ move, subsequentMoves, player }, index) => {
|
||||||
|
subsequentMoves.forEach(({ player, move }, subIndex) => {
|
||||||
|
ctx2.beginPath();
|
||||||
|
ctx2.arc(
|
||||||
|
subIndex * space + offset,
|
||||||
|
index * space + offset,
|
||||||
|
offset * 0.95,
|
||||||
|
0,
|
||||||
|
Math.PI * 2,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
ctx2.stroke();
|
||||||
|
ctx2.fillStyle = player === "white" ? "#fff" : "#000";
|
||||||
|
ctx2.fill();
|
||||||
|
ctx2.fillStyle = player === "white" ? "#000" : "#fff";
|
||||||
|
ctx2.fillText(move, subIndex * space + offset, index * space + offset);
|
||||||
|
});
|
||||||
|
ctx2.fillStyle = "#000";
|
||||||
|
ctx2.fillText(
|
||||||
|
"at",
|
||||||
|
subsequentMoves.length * space + offset,
|
||||||
|
index * space + offset
|
||||||
|
);
|
||||||
|
ctx2.fillStyle = player === "white" ? "#fff" : "#000";
|
||||||
|
ctx2.beginPath();
|
||||||
|
ctx2.arc(
|
||||||
|
(subsequentMoves.length + 1) * space + offset,
|
||||||
|
index * space + offset,
|
||||||
|
offset * 0.95,
|
||||||
|
0,
|
||||||
|
Math.PI * 2,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
ctx2.fill();
|
||||||
|
ctx2.stroke();
|
||||||
|
ctx2.fillStyle = player === "white" ? "#000" : "#fff";
|
||||||
|
ctx2.fillText(
|
||||||
|
move,
|
||||||
|
(subsequentMoves.length + 1) * space + offset,
|
||||||
|
index * space + offset
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, [showMenu, meta, active.handicap, boardSize]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`Game__Menu-container${showMenu ? "" : "--hidden"}`}
|
||||||
|
onClick={(e) => handleBackgroundClick(e)}
|
||||||
|
>
|
||||||
|
<div className="Game__Menu-container__Menu">
|
||||||
|
<button onClick={clickClose}>X</button>
|
||||||
|
<button onClick={clickPrint}>Print Record</button>
|
||||||
|
<div id="record" className="Game__Menu__game-record-container">
|
||||||
|
{drawGameRecord()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Menu;
|
29
packages/play-node-go/src/components/GameUI/Menu/Menu.scss
Normal file
29
packages/play-node-go/src/components/GameUI/Menu/Menu.scss
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
div.Game__Menu-container {
|
||||||
|
&--hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
position: absolute;
|
||||||
|
width: 100vw;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.Game__Menu-container__Menu {
|
||||||
|
background: #eef;
|
||||||
|
max-height: 90vh;
|
||||||
|
max-width: 80vw;
|
||||||
|
padding: 2vh;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.Game__Menu__game-record-container {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,45 +1,66 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import './PlayerArea.scss';
|
import "./PlayerArea.scss";
|
||||||
|
|
||||||
const PlayerArea = (props) => {
|
const PlayerArea = ({
|
||||||
// const { user } = props
|
handleResignClick,
|
||||||
const user = {
|
handlePassClick,
|
||||||
stones: 'black',
|
playerMeta,
|
||||||
username: 'Name',
|
turn,
|
||||||
captures: 0
|
Kifu,
|
||||||
}
|
}) => {
|
||||||
|
const { stones, player, rank, captures } = playerMeta;
|
||||||
|
const isTurn =
|
||||||
|
(stones === "black" && turn === 1) || (stones === "white" && turn === -1);
|
||||||
|
const bowlAttributes = () => {
|
||||||
|
if (isTurn || turn === 0)
|
||||||
|
return {
|
||||||
|
"data-turn": true,
|
||||||
|
onClick: () => handlePassClick(stones),
|
||||||
|
};
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
const bowlText = () => {
|
||||||
|
if (isTurn) return "Pass?";
|
||||||
|
if (turn === 0) return "End Game?";
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={`player-container player-container--${stones}`}>
|
||||||
<div
|
<div
|
||||||
className={`player-container player-container--${user.stones}`}
|
className={`player-container__bowl player-container__bowl--${stones}`}
|
||||||
|
{...bowlAttributes()}
|
||||||
>
|
>
|
||||||
<div
|
<p>{bowlText()}</p>
|
||||||
className={`player-container__bowl player-container__bowl--${user.stones}`}
|
|
||||||
>
|
|
||||||
<p>Pass?</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
{Kifu}
|
||||||
<div
|
<div
|
||||||
className={`player-container__name-space player-container__name-space--${user.stones}`}
|
className={`player-container__name-space player-container__name-space--${stones}`}
|
||||||
>
|
>
|
||||||
<h4>{user ? user.username : 'Waiting for player' }</h4>
|
<h4>
|
||||||
|
{playerMeta
|
||||||
|
? `${player || stones} ${rank || "?"}`
|
||||||
|
: "Waiting for player"}
|
||||||
|
</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`player-container__caps-space player-container__caps-space__${user.stones}`}
|
className={`player-container__caps-space player-container__caps-space__${stones}`}
|
||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
className={`player-container__resign-message player-container__resign-message--${user.stones}`}
|
className={`player-container__resign-message player-container__resign-message--${stones}`}
|
||||||
>Resign?</p>
|
{...(isTurn ? { onClick: () => handleResignClick(stones) } : null)}
|
||||||
|
>
|
||||||
|
Resign?
|
||||||
|
</p>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
className={`player-container__caps-counter player-container__caps-counter--${user.stones}`}
|
className={`player-container__caps-counter player-container__caps-counter--${stones}`}
|
||||||
>{user ? user.captures : 'Captures go here'}</p>
|
>
|
||||||
|
{playerMeta ? captures : "Captures go here"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default PlayerArea;
|
export default PlayerArea;
|
|
@ -47,6 +47,7 @@ div.player-container {
|
||||||
padding: .5em;
|
padding: .5em;
|
||||||
}
|
}
|
||||||
&[data-turn] {
|
&[data-turn] {
|
||||||
|
// highlight for turn
|
||||||
box-shadow: 0 0 3vh 3vh rgb(255, 175, 2);
|
box-shadow: 0 0 3vh 3vh rgb(255, 175, 2);
|
||||||
|
|
||||||
& + .player-container__name-space .player-container__caps-space:hover :first-child {
|
& + .player-container__name-space .player-container__caps-space:hover :first-child {
|
||||||
|
|
|
@ -1,58 +1,96 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import './Point.scss';
|
import "./Point.scss";
|
||||||
|
|
||||||
const Point = (props) => {
|
const Point = (props) => {
|
||||||
const { posX, posY, user, game, dispatch, pointData } = props;
|
const {
|
||||||
const turn = game.turn > 0 ? 'black' : 'white';
|
posX,
|
||||||
|
posY,
|
||||||
|
user,
|
||||||
|
game,
|
||||||
|
meta,
|
||||||
|
dispatch,
|
||||||
|
pointData,
|
||||||
|
dotData,
|
||||||
|
hoshi,
|
||||||
|
} = props;
|
||||||
|
const turn =
|
||||||
|
meta && meta.turn
|
||||||
|
? meta.turn > 0
|
||||||
|
? "black"
|
||||||
|
: "white"
|
||||||
|
: game.turn > 0
|
||||||
|
? "black"
|
||||||
|
: "white";
|
||||||
|
|
||||||
const stone = () => {
|
const stone = () => {
|
||||||
if (pointData === 1) return 'black'
|
if (pointData === 1) return "black";
|
||||||
if (pointData === -1) return 'white'
|
if (pointData === -1) return "white";
|
||||||
return 'none'
|
if (pointData === "k") return "ko";
|
||||||
}
|
return "none";
|
||||||
|
};
|
||||||
const dot = () => {
|
|
||||||
if (pointData === 'l') return game.turn;
|
|
||||||
}
|
|
||||||
|
|
||||||
const xFlag = () => {
|
const xFlag = () => {
|
||||||
if ( posX === 1 ) return `board__point--top`
|
if (posX === 1) return `board__point--top`;
|
||||||
if ( posX === game.boardSize ) return `board__point--bottom`
|
if (posX === game.boardSize) return `board__point--bottom`;
|
||||||
return '';
|
return "";
|
||||||
}
|
};
|
||||||
const yFlag = () => {
|
const yFlag = () => {
|
||||||
if ( posY === 1 ) return `board__point--left`
|
if (posY === 1) return `board__point--left`;
|
||||||
if ( posY === game.boardSize ) return `board__point--right`
|
if (posY === game.boardSize) return `board__point--right`;
|
||||||
return '';
|
return "";
|
||||||
}
|
};
|
||||||
const clickHandle = (e) => {
|
const clickHandle = (e) => {
|
||||||
|
if (meta?.turn === 0 && !meta?.winner) {
|
||||||
const action = {
|
const action = {
|
||||||
type: 'SOCKET',
|
type: "SOCKET",
|
||||||
message: 'MAKE_MOVE',
|
message: "TOGGLE_TERRITORY",
|
||||||
|
body: { user, point: `${posX}-${posY}`, game, room: game.room },
|
||||||
|
};
|
||||||
|
return dispatch(action);
|
||||||
|
}
|
||||||
|
const action = {
|
||||||
|
type: "SOCKET",
|
||||||
|
message: "MAKE_MOVE",
|
||||||
body: {
|
body: {
|
||||||
user,
|
user,
|
||||||
game,
|
game,
|
||||||
room: game.room,
|
room: game.room,
|
||||||
board: {},
|
board: {},
|
||||||
move: { player: turn, pos: { x: posX, y: posY } }
|
move: { player: turn, pos: { x: posX, y: posY } },
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
dispatch(action);
|
dispatch(action);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDot = () => {
|
||||||
|
if (meta?.turn === 0) {
|
||||||
|
switch (dotData) {
|
||||||
|
case -1:
|
||||||
|
return "white";
|
||||||
|
case 1:
|
||||||
|
return "black";
|
||||||
|
case "d":
|
||||||
|
return "dame";
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return dotData;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`board__point ${xFlag()} ${yFlag()}`}
|
className={`board__point ${xFlag()} ${yFlag()}`}
|
||||||
onClick={e => clickHandle(e)}
|
onClick={(e) => clickHandle(e)}
|
||||||
>
|
>
|
||||||
<div className="board__point__stone"
|
<div
|
||||||
|
className={`board__point__stone ${hoshi ? "hoshi" : ""}`}
|
||||||
data-stone={stone()}
|
data-stone={stone()}
|
||||||
>
|
>
|
||||||
<div className="board__point__dot" data-dot={dot()}></div>
|
<div className="board__point__dot" data-dot={getDot()}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Point;
|
export default Point;
|
|
@ -5,6 +5,9 @@ div.board__point {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.board__point--top {
|
div.board__point--top {
|
||||||
|
@ -43,11 +46,14 @@ div.board__point__stone {
|
||||||
width: 85%;
|
width: 85%;
|
||||||
height: 85%;
|
height: 85%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin: auto;
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
&.hoshi {
|
||||||
|
background: radial-gradient(circle farthest-corner at center, #000 0%, #000 14%, rgba(0,0,0,0) 15%);
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.board__point div.board__point__stone div.board__point__dot {
|
div.board__point div.board__point__stone div.board__point__dot {
|
||||||
|
@ -58,13 +64,44 @@ div.board__point div.board__point__stone div.board__point__dot {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.board__point__stone[data-stone="white"] {
|
div.board__point__stone{
|
||||||
|
&[data-stone="white"] {
|
||||||
background: radial-gradient(farthest-side at 55% 40%, white 0%, rgb(200,200,200) 65%, rgb(100,100,100) 90%, rgb(68, 50, 0) 100%);
|
background: radial-gradient(farthest-side at 55% 40%, white 0%, rgb(200,200,200) 65%, rgb(100,100,100) 90%, rgb(68, 50, 0) 100%);
|
||||||
box-shadow: -.25vmin .5vmin .5vmin rgba(145, 92, 23, 0.5);
|
box-shadow: -.25vmin .5vmin .5vmin rgba(145, 92, 23, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
div.board__point__stone[data-stone="black"] {
|
&[data-stone="black"] {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
background: radial-gradient(farthest-side at 55% 40%, rgb(220,220,220) 0%, rgb(60,60,60) 45%, rgb(15,15,15) 90%, rgb(5, 5, 0) 100%);
|
background: radial-gradient(farthest-side at 55% 40%, rgb(220,220,220) 0%, rgb(60,60,60) 45%, rgb(15,15,15) 90%, rgb(5, 5, 0) 100%);
|
||||||
box-shadow: -.25vmin .5vmin .5vmin rgba(145, 92, 23, 0.75);
|
box-shadow: -.25vmin .5vmin .5vmin rgba(145, 92, 23, 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[data-stone="ko"] {
|
||||||
|
background-color: transparent;
|
||||||
|
border: .5vmin solid rgba(200,20,50,0.8);
|
||||||
|
border-radius: 0%;
|
||||||
|
height: 60%;
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.board__point {
|
||||||
|
div.board__point__dot[data-dot="dame"] {
|
||||||
|
background: purple;
|
||||||
|
}
|
||||||
|
div.board__point__dot[data-dot="black"] {
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
div.board__point__dot[data-dot="white"] {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
div.board__point__dot[data-dot="1"] {
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
div.board__point__dot[data-dot="-1"] {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,43 +1,56 @@
|
||||||
import socketIOClient from 'socket.io-client';
|
import socketIOClient from "socket.io-client";
|
||||||
import config from './config';
|
import config from "./config";
|
||||||
|
|
||||||
const launch = (nsp, dispatch) => {
|
const launch = (nsp, dispatch) => {
|
||||||
const socket = socketIOClient(`${config.socketAddress}/${nsp}`);
|
const socket = socketIOClient(`${config.socketAddress}/${nsp}`);
|
||||||
|
|
||||||
socket.on('connected', () => {
|
socket.on("connected", () => {
|
||||||
dispatch({ type:'SOCKET', message:'CONNECTED', body:{nsp: socket.nsp} });
|
dispatch({
|
||||||
|
type: "SOCKET",
|
||||||
|
message: "CONNECTED",
|
||||||
|
body: { nsp: socket.nsp },
|
||||||
});
|
});
|
||||||
socket.on('connect_error', err => {
|
|
||||||
dispatch({ type: 'ERR', message:'SOCKET_ERROR', body: { socketError: err }});
|
|
||||||
});
|
});
|
||||||
socket.on('error', err => {
|
socket.on("connect_error", (err) => {
|
||||||
dispatch({ type: 'ERR', message:'SOCKET_ERROR', body: { socketError: err } });
|
dispatch({
|
||||||
|
type: "ERR",
|
||||||
|
message: "SOCKET_ERROR",
|
||||||
|
body: { socketError: err },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
socket.on("error", (err) => {
|
||||||
|
dispatch({
|
||||||
|
type: "ERR",
|
||||||
|
message: "SOCKET_ERROR",
|
||||||
|
body: { socketError: err },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('room_connected', (data) => {
|
socket.on("room_connected", (data) => {
|
||||||
dispatch({ type: 'ROOMS', message: 'CONNECT_ROOM', body: data });
|
dispatch({ type: "ROOMS", message: "CONNECT_ROOM", body: data });
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('new_user', (data) => {
|
socket.on("new_user", (data) => {
|
||||||
console.log('new_user received')
|
dispatch({ type: "ROOMS", message: "NEW_USER", body: data });
|
||||||
dispatch({ type: 'ROOMS', message: 'NEW_USER', body: data })
|
});
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('game_connected', (data) => {
|
socket.on("game_connected", (data) => {
|
||||||
console.log(data)
|
dispatch({ type: "GAMES", message: "UPDATE_BOARD", body: data });
|
||||||
console.log('game_connected received')
|
});
|
||||||
dispatch({ type: 'GAMES', message: 'UPDATE_BOARD', body: data })
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('update_board', (data) => {
|
socket.on("update_board", (data) => {
|
||||||
console.log(data)
|
dispatch({ type: "GAMES", message: "UPDATE_BOARD", body: data });
|
||||||
console.log('update_board received')
|
});
|
||||||
dispatch({ type: 'GAMES', message: 'UPDATE_BOARD', body: data.board })
|
|
||||||
})
|
socket.on("game_resign", (data) => {
|
||||||
|
dispatch({ type: "GAMES", message: "GAME_RESIGN", body: data });
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("end_game", (data) => {
|
||||||
|
dispatch({ type: "GAMES", message: "GAME_END", body: data });
|
||||||
|
});
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
}
|
};
|
||||||
|
|
||||||
export {
|
export { launch };
|
||||||
launch
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,70 +1,155 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from "react";
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from "react-router-dom";
|
||||||
import gamesServices from '../../services/api/gamesServices';
|
import gamesServices from "../../services/api/gamesServices";
|
||||||
import './Game.scss';
|
import "./Game.scss";
|
||||||
import Logo from '../../components/Display/Logo/Logo';
|
import Logo from "../../components/Display/Logo/Logo";
|
||||||
import Board from '../../components/GameUI/Board/Board';
|
import Board from "../../components/GameUI/Board/Board";
|
||||||
import PlayerArea from '../../components/GameUI/PlayerArea/PlayerArea';
|
import PlayerArea from "../../components/GameUI/PlayerArea/PlayerArea";
|
||||||
|
import Kifu from "../../components/GameUI/Kifu/Kifu";
|
||||||
|
import Menu from "../../components/GameUI/Menu/Menu";
|
||||||
|
|
||||||
const Game = (props) => {
|
const Game = (props) => {
|
||||||
const { state, dispatch } = props;
|
const { state, dispatch } = props;
|
||||||
const gameId = parseInt(useParams().id) || 0;
|
const gameId = parseInt(useParams().id) || 0;
|
||||||
|
const [playerBlackMeta, setPlayerBlackMeta] = useState({});
|
||||||
|
const [playerWhiteMeta, setPlayerWhiteMeta] = useState({});
|
||||||
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
|
const playerState = state?.meta?.playerState;
|
||||||
|
const game = state.active?.game;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchGameAPI = async () => {
|
const fetchGameAPI = async () => {
|
||||||
const response = await gamesServices.getGameService(gameId);
|
const response = await gamesServices.getGameService(gameId);
|
||||||
if (response) {
|
if (response) {
|
||||||
const action = {
|
const action = {
|
||||||
type: 'GAMES',
|
type: "GAMES",
|
||||||
message: 'SET_ACTIVE',
|
message: "SET_ACTIVE",
|
||||||
body: response
|
body: response,
|
||||||
}
|
};
|
||||||
return dispatch(action);
|
return dispatch(action);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
fetchGameAPI();
|
fetchGameAPI();
|
||||||
}, [ gameId, dispatch ])
|
}, [gameId, dispatch]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const roomSocketConnect = () => {
|
const roomSocketConnect = () => {
|
||||||
const game = state.active.game;
|
const game = state.active.game;
|
||||||
const user = state.user;
|
const user = state.user;
|
||||||
const action = {
|
const action = {
|
||||||
type: 'SOCKET',
|
type: "SOCKET",
|
||||||
message: 'CONNECT_GAME',
|
message: "CONNECT_GAME",
|
||||||
body: { game, user, dispatch }
|
body: { game, user, dispatch },
|
||||||
}
|
};
|
||||||
|
return dispatch(action);
|
||||||
|
};
|
||||||
|
roomSocketConnect();
|
||||||
|
}, [state.active.game, dispatch, state.user]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!game || !playerState) return;
|
||||||
|
const { playerBlack, playerBlackRank, playerWhite, playerWhiteRank } = game;
|
||||||
|
const { bCaptures, wCaptures } = playerState;
|
||||||
|
setPlayerBlackMeta({
|
||||||
|
player: playerBlack,
|
||||||
|
rank: playerBlackRank,
|
||||||
|
captures: bCaptures,
|
||||||
|
stones: "black",
|
||||||
|
});
|
||||||
|
setPlayerWhiteMeta({
|
||||||
|
player: playerWhite,
|
||||||
|
rank: playerWhiteRank,
|
||||||
|
captures: wCaptures,
|
||||||
|
stones: "white",
|
||||||
|
});
|
||||||
|
}, [playerState, game]);
|
||||||
|
|
||||||
|
const handleResignClick = (player) => {
|
||||||
|
const action = {
|
||||||
|
type: "SOCKET",
|
||||||
|
message: "RESIGN",
|
||||||
|
body: { game, player },
|
||||||
|
};
|
||||||
|
dispatch(action);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePassClick = (player) => {
|
||||||
|
if (state?.meta && state?.meta?.winner) return;
|
||||||
|
if (state?.meta && state?.meta?.turn === 0) {
|
||||||
|
const action = {
|
||||||
|
type: "SOCKET",
|
||||||
|
message: "END_GAME",
|
||||||
|
body: { game, player },
|
||||||
|
};
|
||||||
return dispatch(action);
|
return dispatch(action);
|
||||||
}
|
}
|
||||||
roomSocketConnect();
|
const action = {
|
||||||
}, [ state.active , dispatch, state.user ] )
|
type: "SOCKET",
|
||||||
|
message: "PASS",
|
||||||
|
body: { game, player },
|
||||||
|
};
|
||||||
|
dispatch(action);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="Game" data-testid="Game">
|
||||||
className="Game"
|
<Menu
|
||||||
data-testid="Game"
|
showMenu={showMenu}
|
||||||
>
|
clickClose={() => setShowMenu(false)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
<div className="Game__meta-container">
|
<div className="Game__meta-container">
|
||||||
<span
|
<span className="Game__socket-flag">{state.socket ? "✓" : " ⃠"}</span>
|
||||||
className="Game__socket-flag"
|
|
||||||
>{state.socket ? '✓' : ' ⃠'}</span>
|
|
||||||
<Logo />
|
<Logo />
|
||||||
|
{state?.meta?.winner ? (
|
||||||
|
<p>
|
||||||
|
{`winner: ${
|
||||||
|
state.meta.winner === 1
|
||||||
|
? playerBlackMeta?.player
|
||||||
|
: playerWhiteMeta?.player
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
<p>Timer</p>
|
<p>Timer</p>
|
||||||
<p>? Game Tree</p>
|
<p>? Game Tree</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="Game__board-container">
|
<div className="Game__board-container">
|
||||||
<PlayerArea />
|
<PlayerArea
|
||||||
|
handleResignClick={handleResignClick}
|
||||||
|
handlePassClick={handlePassClick}
|
||||||
|
playerMeta={
|
||||||
|
state.user &&
|
||||||
|
playerBlackMeta.playerBlack &&
|
||||||
|
state.user === playerBlackMeta.playerBlack
|
||||||
|
? playerBlackMeta
|
||||||
|
: playerWhiteMeta
|
||||||
|
}
|
||||||
|
turn={state?.meta?.turn}
|
||||||
|
/>
|
||||||
<Board
|
<Board
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
game={state.active.game}
|
game={state.active.game}
|
||||||
record={state.active.record}
|
meta={state.meta}
|
||||||
user={state.user}
|
user={state.user}
|
||||||
board={state.board}
|
board={state.board}
|
||||||
/>
|
/>
|
||||||
<PlayerArea />
|
<PlayerArea
|
||||||
|
handleResignClick={handleResignClick}
|
||||||
|
handlePassClick={handlePassClick}
|
||||||
|
playerMeta={
|
||||||
|
state.user &&
|
||||||
|
playerBlackMeta.playerWhite &&
|
||||||
|
state.user === playerWhiteMeta.playerWhite
|
||||||
|
? playerWhiteMeta
|
||||||
|
: playerBlackMeta
|
||||||
|
}
|
||||||
|
Kifu={<Kifu clickKifu={() => setShowMenu(true)} />}
|
||||||
|
turn={state?.meta?.turn}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="Game__message-container">
|
<div className="Game__message-container">
|
||||||
|
@ -73,6 +158,6 @@ const Game = (props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Game;
|
export default Game;
|
|
@ -1,4 +1,5 @@
|
||||||
@import '../../../../public/stylesheets/partials/mixins';
|
@import '../../../../public/stylesheets/partials/mixins';
|
||||||
|
@import '../../../../public/stylesheets/partials/variables';
|
||||||
|
|
||||||
div.main-wrapper {
|
div.main-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -34,3 +35,14 @@ div.main-wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color:map-get($colors, 'sidebar_link');
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 110%;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
padding: 0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
|
@ -1,29 +1,36 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
import './NavBar.scss';
|
import "./NavBar.scss";
|
||||||
import Logo from '../../../components/Display/Logo/Logo';
|
import Logo from "../../../components/Display/Logo/Logo";
|
||||||
|
|
||||||
const NavBar = (props) => {
|
|
||||||
|
|
||||||
|
const NavBar = ({ state }) => {
|
||||||
return (
|
return (
|
||||||
<div className="NavBar" data-testid="NavBar">
|
<div className="NavBar" data-testid="NavBar">
|
||||||
<Link to="/home">
|
<Link to="/home">
|
||||||
<div className="NavBar__logo"><Logo /></div>
|
<div className="NavBar__logo">
|
||||||
|
<Logo />
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to="/home">
|
<Link to="/home">
|
||||||
<div className="NavBar__menu-item NavBar__home"><p className="--link">Find a Game</p></div>
|
<div className="NavBar__menu-item NavBar__home">
|
||||||
|
<p className="--link">Find a Game</p>
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to="/news">
|
<Link to="/news">
|
||||||
<div className="NavBar__menu-item NavBar__news"><p className="--link">News</p></div>
|
<div className="NavBar__menu-item NavBar__news">
|
||||||
|
<p className="--link">News</p>
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to="/account">
|
<Link to="/account">
|
||||||
<div className="NavBar__menu-item NavBar__account">{props.user ? props.user.username : <></>}</div>
|
<div className="NavBar__menu-item NavBar__account">
|
||||||
|
{state.user.username ? state.user.username : <></>}
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default NavBar;
|
export default NavBar;
|
|
@ -1,18 +1,21 @@
|
||||||
export const authReducer = (state, action) => {
|
export const authReducer = (state, action) => {
|
||||||
switch (action.message) {
|
switch (action.message) {
|
||||||
case 'LOGIN':
|
case "LOGIN":
|
||||||
return loginReducer(state, action);
|
return loginReducer(state, action);
|
||||||
|
|
||||||
case 'SIGNUP':
|
case "SIGNUP":
|
||||||
return loginReducer(state, action);
|
return loginReducer(state, action);
|
||||||
|
|
||||||
case 'LOGOUT':
|
case "GUEST":
|
||||||
|
return loginReducer(state, action);
|
||||||
|
|
||||||
|
case "LOGOUT":
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function loginReducer(state, action) {
|
function loginReducer(state, action) {
|
||||||
const newUser = action.body;
|
const newUser = action.body;
|
||||||
|
|
|
@ -1,62 +1,104 @@
|
||||||
import { stateReducer } from '../reducer';
|
import { stateReducer } from "../reducer";
|
||||||
|
|
||||||
export const gamesReducer = (state, action) => {
|
export const gamesReducer = (state, action) => {
|
||||||
switch (action.message) {
|
switch (action.message) {
|
||||||
|
case "SET_GAMES": {
|
||||||
case 'SET_GAMES':
|
const games = formatGames(action);
|
||||||
const games = formatGames(action);;
|
|
||||||
return { ...state, games };
|
return { ...state, games };
|
||||||
|
|
||||||
case 'JOIN_REQUEST':
|
|
||||||
if (!Object.entries(state.user).length) {
|
|
||||||
const errAction = {
|
|
||||||
type: 'ERR',
|
|
||||||
message: 'JOIN_GAME_ERROR',
|
|
||||||
body: {joinGameError: 'user not logged in'}
|
|
||||||
}
|
}
|
||||||
return stateReducer(state, errAction)
|
|
||||||
|
case "JOIN_REQUEST": {
|
||||||
|
return joinRequest(state, action);
|
||||||
}
|
}
|
||||||
const id = action.body;
|
|
||||||
return {...state, joinGame: id};
|
|
||||||
|
|
||||||
case 'UPDATE_BOARD':
|
case "UPDATE_BOARD": {
|
||||||
console.log(action.body)
|
return updateBoard(state, action);
|
||||||
return {...state, board: action.body};
|
}
|
||||||
|
|
||||||
case 'SET_ACTIVE':
|
case "GAME_RESIGN": {
|
||||||
|
return gameResign(state, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "SET_ACTIVE": {
|
||||||
return { ...state, active: action.body };
|
return { ...state, active: action.body };
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
case "GAME_END": {
|
||||||
|
return gameEnd(state, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// parse ranks from db in K9 format to 9k format
|
||||||
function parseRank(rank) {
|
function parseRank(rank) {
|
||||||
switch (rank[0]) {
|
switch (rank[0]) {
|
||||||
case 'D':
|
// Dan ranks
|
||||||
return `${rank.slice(1)}${rank[0].toLowerCase()}`
|
case "D":
|
||||||
case 'K':
|
return `${rank.slice(1)}${rank[0].toLowerCase()}`;
|
||||||
return `${rank.slice(1)}${rank[0].toLowerCase()}`
|
// Kyu ranks
|
||||||
case 'U':
|
case "K":
|
||||||
return '?'
|
return `${rank.slice(1)}${rank[0].toLowerCase()}`;
|
||||||
|
// Unranked
|
||||||
|
case "U":
|
||||||
|
return "?";
|
||||||
default:
|
default:
|
||||||
return '?'
|
return "?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatGames(action) {
|
function formatGames(action) {
|
||||||
const games = [...action.body].map(game => {
|
const games = [...action.body].map((game) => {
|
||||||
|
|
||||||
if (game.playerBlackRank) {
|
if (game.playerBlackRank) {
|
||||||
game.playerBlackRank = parseRank(game.playerBlackRank)
|
game.playerBlackRank = parseRank(game.playerBlackRank);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.playerWhiteRank) {
|
if (game.playerWhiteRank) {
|
||||||
game.playerWhiteRank = parseRank(game.playerWhiteRank)
|
game.playerWhiteRank = parseRank(game.playerWhiteRank);
|
||||||
}
|
}
|
||||||
|
|
||||||
return game;
|
return game;
|
||||||
})
|
});
|
||||||
|
|
||||||
return games;
|
return games;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function joinRequest(state, action) {
|
||||||
|
if (!Object.entries(state.user).length) {
|
||||||
|
const errAction = {
|
||||||
|
type: "ERR",
|
||||||
|
message: "JOIN_GAME_ERROR",
|
||||||
|
body: { joinGameError: "user not logged in" },
|
||||||
|
};
|
||||||
|
return stateReducer(state, errAction);
|
||||||
|
}
|
||||||
|
const id = action.body;
|
||||||
|
return { ...state, joinGame: id };
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBoard(state, action) {
|
||||||
|
console.log(action.body);
|
||||||
|
const { gameRecord, pass, turn, winner, playerState } = action.body.meta;
|
||||||
|
const territory = action.body.territory;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
board: action.body.board,
|
||||||
|
meta: { gameRecord, pass, turn, winner, playerState, territory },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameResign(state, action) {
|
||||||
|
const { gameRecord, pass, turn, winner, playerState } = action.body;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
meta: { gameRecord, pass, turn, winner, playerState },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameEnd(state, action) {
|
||||||
|
const { winner, score } = action.body.meta;
|
||||||
|
return { ...state, meta: { ...state.meta, winner, score } };
|
||||||
|
}
|
||||||
|
|
|
@ -1,45 +1,59 @@
|
||||||
import { stateReducer } from '../reducer';
|
import { stateReducer } from "../reducer";
|
||||||
const io = require('../../io');
|
const io = require("../../io");
|
||||||
|
|
||||||
export const socketReducer = (state, action) => {
|
export const socketReducer = (state, action) => {
|
||||||
switch (action.message) {
|
switch (action.message) {
|
||||||
|
case "CONNECTED":
|
||||||
|
return { ...state, connect: { type: "home", location: action.body.nsp } };
|
||||||
|
|
||||||
case 'CONNECTED':
|
case "LAUNCH": {
|
||||||
console.log(action.body.nsp)
|
|
||||||
return {...state, connect: { type: 'home', location: action.body.nsp } }
|
|
||||||
|
|
||||||
case 'LAUNCH': {
|
|
||||||
const { nsp, dispatch } = action.body;
|
const { nsp, dispatch } = action.body;
|
||||||
const launchedSocket = io.launch(nsp, dispatch);
|
const launchedSocket = io.launch(nsp, dispatch);
|
||||||
return { ...state, socket: launchedSocket };
|
return { ...state, socket: launchedSocket };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'CONNECT_ROOM': {
|
case "CONNECT_ROOM": {
|
||||||
const { user, room, dispatch } = action.body;
|
const { user, room, dispatch } = action.body;
|
||||||
let priorSocket = state.socket;
|
let priorSocket = state.socket;
|
||||||
if (!priorSocket.nsp) {
|
if (!priorSocket.nsp) {
|
||||||
priorSocket = io.launch('', dispatch)
|
priorSocket = io.launch("", dispatch);
|
||||||
}
|
}
|
||||||
if (priorSocket.nsp !== `/${room}`) {
|
if (priorSocket.nsp !== `/${room}`) {
|
||||||
priorSocket.emit('connect_room', {user, room});
|
priorSocket.emit("connect_room", { user, room });
|
||||||
priorSocket.close();
|
priorSocket.close();
|
||||||
}
|
}
|
||||||
const socket = io.launch(room, dispatch);
|
const socket = io.launch(room, dispatch);
|
||||||
return {...state, socket}
|
return { ...state, socket };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'CONNECT_GAME': {
|
case "CONNECT_GAME": {
|
||||||
return connectGame(state, action);
|
return connectGame(state, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'MAKE_MOVE': {
|
case "MAKE_MOVE": {
|
||||||
return makeMove(state, action);
|
return makeMove(state, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "RESIGN": {
|
||||||
|
return resign(state, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "PASS": {
|
||||||
|
return pass(state, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "TOGGLE_TERRITORY": {
|
||||||
|
return toggleTerritory(state, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "END_GAME": {
|
||||||
|
return endGame(state, action);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function connectGame(state, action) {
|
function connectGame(state, action) {
|
||||||
const { user, game, dispatch } = action.body;
|
const { user, game, dispatch } = action.body;
|
||||||
|
@ -47,23 +61,45 @@ function connectGame (state, action) {
|
||||||
let updatedState;
|
let updatedState;
|
||||||
if (!priorSocket.nsp || priorSocket.nsp !== `/${game.room}`) {
|
if (!priorSocket.nsp || priorSocket.nsp !== `/${game.room}`) {
|
||||||
const connectRoomAction = {
|
const connectRoomAction = {
|
||||||
type: 'SOCKET',
|
type: "SOCKET",
|
||||||
message: 'CONNECT_ROOM',
|
message: "CONNECT_ROOM",
|
||||||
body: { user, room: game.room, dispatch}
|
body: { user, room: game.room, dispatch },
|
||||||
}
|
};
|
||||||
updatedState = stateReducer(state, connectRoomAction);
|
updatedState = stateReducer(state, connectRoomAction);
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!updatedState) updatedState = { ...state };
|
if (!updatedState) updatedState = { ...state };
|
||||||
const socket = updatedState.socket;
|
const socket = updatedState.socket;
|
||||||
socket.emit('connect_game', {user, game});
|
socket.emit("connect_game", { user, game });
|
||||||
return { ...updatedState };
|
return { ...updatedState };
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeMove(state, action) {
|
function makeMove(state, action) {
|
||||||
// const { user, game, room, board, move } = action.body;
|
|
||||||
const socket = state.socket;
|
const socket = state.socket;
|
||||||
console.log(action)
|
socket.emit("make_move", { ...action.body });
|
||||||
socket.emit('make_move', {...action.body});
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resign(state, action) {
|
||||||
|
const socket = state.socket;
|
||||||
|
socket.emit("resign", { ...action.body });
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pass(state, action) {
|
||||||
|
const socket = state.socket;
|
||||||
|
socket.emit("pass", { ...action.body });
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTerritory(state, action) {
|
||||||
|
const socket = state.socket;
|
||||||
|
socket.emit("toggle_territory", { ...action.body });
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function endGame(state, action) {
|
||||||
|
console.log("end game");
|
||||||
|
const socket = state.socket;
|
||||||
|
socket.emit("end_game", { ...action.body });
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
|
@ -1,43 +1,58 @@
|
||||||
import config from '../config';
|
import config from "../config";
|
||||||
|
|
||||||
const authEndpoint = config.authAddress;
|
const authEndpoint = config.authAddress;
|
||||||
const signupEndpoint = `${authEndpoint}/signup`
|
const signupEndpoint = `${authEndpoint}/signup`;
|
||||||
const loginEndpoint = `${authEndpoint}/login`
|
const loginEndpoint = `${authEndpoint}/login`;
|
||||||
|
const guestEndpoint = `${authEndpoint}/guest`;
|
||||||
|
|
||||||
var headers = new Headers();
|
var headers = new Headers();
|
||||||
headers.append('Content-Type', 'application/json');
|
headers.append("Content-Type", "application/json");
|
||||||
headers.append('Accept', 'application/json');
|
headers.append("Accept", "application/json");
|
||||||
headers.append('Sec-Fetch-Site', 'cross-site')
|
headers.append("Sec-Fetch-Site", "cross-site");
|
||||||
|
|
||||||
const loginService = async (formData) => {
|
const loginService = async (formData) => {
|
||||||
const response = await fetch(loginEndpoint, {
|
const response = await fetch(loginEndpoint, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
credentials: 'include',
|
credentials: "include",
|
||||||
body: JSON.stringify(formData),
|
body: JSON.stringify(formData),
|
||||||
headers: headers
|
headers: headers,
|
||||||
})
|
})
|
||||||
.then(res => res.text())
|
.then((res) => res.text())
|
||||||
.then(text => JSON.parse(text))
|
.then((text) => JSON.parse(text))
|
||||||
.catch(err => err);
|
.catch((err) => err);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
};
|
||||||
|
|
||||||
const signupService = async (formData) => {
|
const signupService = async (formData) => {
|
||||||
const response = await fetch(signupEndpoint, {
|
const response = await fetch(signupEndpoint, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
credentials: 'include',
|
credentials: "include",
|
||||||
body: JSON.stringify(formData),
|
body: JSON.stringify(formData),
|
||||||
headers: headers
|
headers: headers,
|
||||||
})
|
})
|
||||||
.then(res => res.text())
|
.then((res) => res.text())
|
||||||
.then(text => JSON.parse(text))
|
.then((text) => JSON.parse(text))
|
||||||
.catch(err => err);
|
.catch((err) => err);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const guestService = async () => {
|
||||||
|
const response = await fetch(guestEndpoint, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((text) => JSON.parse(text))
|
||||||
|
.catch((err) => err);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
loginService,
|
loginService,
|
||||||
signupService
|
signupService,
|
||||||
}
|
guestService,
|
||||||
|
};
|
||||||
|
|
|
@ -10,8 +10,9 @@ const show = async (req, res, next) => {
|
||||||
|
|
||||||
// TODO Promise.all()
|
// TODO Promise.all()
|
||||||
const game = await gameQueries.findGameById(gameId);
|
const game = await gameQueries.findGameById(gameId);
|
||||||
const record = await moveQueries.findGameRecord(gameId);
|
// const record = await moveQueries.findGameRecord(gameId);
|
||||||
res.status(200).json({game, record})
|
// console.log(record)
|
||||||
|
res.status(200).json({game})
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
res.status(500).json(err);
|
res.status(500).json(err);
|
||||||
|
|
|
@ -10,8 +10,9 @@ const getAll = async (req, res, next) => {
|
||||||
res.status(200).json({rooms: [...publicRooms]})
|
res.status(200).json({rooms: [...publicRooms]})
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (err) {
|
catch (e) {
|
||||||
res.status(500).json(err);
|
console.log(e)
|
||||||
|
res.status(500).json(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +29,9 @@ const show = async (req, res, next) => {
|
||||||
const body = {currentRoom, messages, roomGames};
|
const body = {currentRoom, messages, roomGames};
|
||||||
res.status(200).json(body);
|
res.status(200).json(body);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (e) {
|
||||||
console.log(err)
|
console.log(e)
|
||||||
res.status(500).json(err);
|
res.status(500).json(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
const { validationResult } = require('express-validator');
|
const { validationResult } = require("express-validator");
|
||||||
|
|
||||||
const userQueries = require('../data/queries/user');
|
const userQueries = require("../data/queries/user");
|
||||||
const { hashPassword, compareHash } = require('../services/bcrypt');
|
const { hashPassword, compareHash } = require("../services/bcrypt");
|
||||||
const signToken = require('../services/signToken');
|
const signToken = require("../services/signToken");
|
||||||
|
const guestServices = require("../services/guestServices");
|
||||||
|
|
||||||
const checkValidationErrors = (req, res) => {
|
const checkValidationErrors = (req, res) => {
|
||||||
const errors = validationResult(req);
|
const errors = validationResult(req);
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
return res.status(422).json({ errors: errors.array() });
|
return res.status(422).json({ errors: errors.array() });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const signup = async (req, res, next) => {
|
const signup = async (req, res, next) => {
|
||||||
checkValidationErrors(req, res);
|
checkValidationErrors(req, res);
|
||||||
|
@ -20,18 +21,18 @@ const signup = async (req, res, next) => {
|
||||||
const hashedPassword = await hashPassword(user.password);
|
const hashedPassword = await hashPassword(user.password);
|
||||||
const secureUser = { ...user, password: hashedPassword };
|
const secureUser = { ...user, password: hashedPassword };
|
||||||
if (existingUser.length) {
|
if (existingUser.length) {
|
||||||
return res.status(409).json({errors: [{auth: 'User already exists!'}]})
|
return res
|
||||||
|
.status(409)
|
||||||
|
.json({ errors: [{ auth: "User already exists!" }] });
|
||||||
}
|
}
|
||||||
|
|
||||||
const newUser = await userQueries.insertUser(secureUser);
|
const newUser = await userQueries.insertUser(secureUser);
|
||||||
signToken(res, newUser)
|
signToken(res, newUser);
|
||||||
res.status(201).json({ ...newUser });
|
res.status(201).json({ ...newUser });
|
||||||
}
|
} catch (err) {
|
||||||
|
|
||||||
catch (err) {
|
|
||||||
res.status(500).json(err);
|
res.status(500).json(err);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const login = async (req, res, next) => {
|
const login = async (req, res, next) => {
|
||||||
checkValidationErrors(req, res);
|
checkValidationErrors(req, res);
|
||||||
|
@ -42,14 +43,14 @@ const login = async (req, res, next) => {
|
||||||
const savedUser = queryResults[0] || null;
|
const savedUser = queryResults[0] || null;
|
||||||
|
|
||||||
if (!savedUser) {
|
if (!savedUser) {
|
||||||
return res.status(401).send({errors: 'bad credentials'});
|
return res.status(401).send({ errors: "bad credentials" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedPassword = savedUser.password;
|
const hashedPassword = savedUser.password;
|
||||||
const passwordMatch = await compareHash(user.password, hashedPassword);
|
const passwordMatch = await compareHash(user.password, hashedPassword);
|
||||||
|
|
||||||
if (!passwordMatch) {
|
if (!passwordMatch) {
|
||||||
return res.status(401).send({errors: 'bad credentials'});
|
return res.status(401).send({ errors: "bad credentials" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const authorizedUser = { ...savedUser };
|
const authorizedUser = { ...savedUser };
|
||||||
|
@ -57,14 +58,31 @@ const login = async (req, res, next) => {
|
||||||
|
|
||||||
signToken(res, authorizedUser);
|
signToken(res, authorizedUser);
|
||||||
res.send({ ...authorizedUser }).status(200);
|
res.send({ ...authorizedUser }).status(200);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send({ errors: e });
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
catch (err) {
|
const guest = async (req, res, next) => {
|
||||||
res.status(500).send({errors: err});
|
try {
|
||||||
}
|
// username generator returns `Guest-${num}`
|
||||||
|
const { username, password } = guestServices.generateGuest();
|
||||||
|
// generateGuestUser();
|
||||||
|
const email = null;
|
||||||
|
// id generator returns `
|
||||||
|
const id = null;
|
||||||
|
const user = { username, email, id, password };
|
||||||
|
signToken(res, user);
|
||||||
|
delete user.password;
|
||||||
|
res.send(user);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
res.status(500).send({ errors: e });
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
signup,
|
signup,
|
||||||
login
|
login,
|
||||||
}
|
guest,
|
||||||
|
};
|
||||||
|
|
|
@ -1,49 +1,81 @@
|
||||||
const winType = [
|
const winType = ["B+R", "B+", "B+T", "W+R", "W+", "W+T", "0", "Void", "?"];
|
||||||
'B+R', 'B+', 'B+T',
|
|
||||||
'W+R', 'W+', 'W+T',
|
|
||||||
'0', 'Void', '?'
|
|
||||||
]
|
|
||||||
|
|
||||||
const rankArray = [
|
const rankArray = [
|
||||||
'D9', 'D8', 'D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1',
|
"D9",
|
||||||
'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10',
|
"D8",
|
||||||
'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20',
|
"D7",
|
||||||
'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', 'UR'
|
"D6",
|
||||||
]
|
"D5",
|
||||||
|
"D4",
|
||||||
|
"D3",
|
||||||
|
"D2",
|
||||||
|
"D1",
|
||||||
|
"K1",
|
||||||
|
"K2",
|
||||||
|
"K3",
|
||||||
|
"K4",
|
||||||
|
"K5",
|
||||||
|
"K6",
|
||||||
|
"K7",
|
||||||
|
"K8",
|
||||||
|
"K9",
|
||||||
|
"K10",
|
||||||
|
"K11",
|
||||||
|
"K12",
|
||||||
|
"K13",
|
||||||
|
"K14",
|
||||||
|
"K15",
|
||||||
|
"K16",
|
||||||
|
"K17",
|
||||||
|
"K18",
|
||||||
|
"K19",
|
||||||
|
"K20",
|
||||||
|
"K21",
|
||||||
|
"K22",
|
||||||
|
"K23",
|
||||||
|
"K24",
|
||||||
|
"K25",
|
||||||
|
"K26",
|
||||||
|
"K27",
|
||||||
|
"K28",
|
||||||
|
"K29",
|
||||||
|
"K30",
|
||||||
|
"UR",
|
||||||
|
];
|
||||||
|
|
||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return knex.schema.createTable("game", table => {
|
return knex.schema.createTable("game", (table) => {
|
||||||
table.increments('id').primary();
|
table.increments("id").primary();
|
||||||
table.datetime('date');
|
table.datetime("date");
|
||||||
table.float('komi').default(6.5);
|
table.float("komi").default(6.5);
|
||||||
table.integer('handicap').default(0);
|
table.integer("handicap").default(0);
|
||||||
table.integer('board_size').default(19);
|
table.integer("board_size").default(19);
|
||||||
table.boolean('open').default(true);
|
table.boolean("open").default(true);
|
||||||
|
|
||||||
table.string('application');
|
table.string("application");
|
||||||
table.string('application_version');
|
table.string("application_version");
|
||||||
table.timestamps(true, true);
|
table.timestamps(true, true);
|
||||||
|
|
||||||
table.string('player_black');
|
table.string("player_black");
|
||||||
table.string('player_white');
|
table.string("player_white");
|
||||||
table.enu('player_black_rank', rankArray).default('UR');
|
table.enu("player_black_rank", rankArray).default("UR");
|
||||||
table.enu('player_white_rank', rankArray).default('UR');
|
table.enu("player_white_rank", rankArray).default("UR");
|
||||||
|
|
||||||
table.string('event');
|
table.string("event");
|
||||||
table.string('name');
|
table.string("name");
|
||||||
table.string('description');
|
table.string("description");
|
||||||
table.integer('round');
|
table.integer("round");
|
||||||
|
|
||||||
table.enu('win_type', winType);
|
table.enu("win_type", winType);
|
||||||
table.float('score');
|
table.float("score");
|
||||||
table.integer('captures_black');
|
table.integer("captures_black");
|
||||||
table.integer('captures_white');
|
table.integer("captures_white");
|
||||||
|
|
||||||
table.integer('user_black').references('id').inTable('user');
|
table.integer("user_black").references("id").inTable("user");
|
||||||
table.integer('user_white').references('id').inTable('user');
|
table.integer("user_white").references("id").inTable("user");
|
||||||
table.integer('room').references('id').inTable('room');
|
table.integer("room").references("id").inTable("room");
|
||||||
table.integer('time_setting').references('id').inTable('time_setting');
|
table.integer("time_setting").references("id").inTable("time_setting");
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.down = knex => knex.schema.dropTableIfExists("game");
|
exports.down = (knex) => knex.schema.dropTableIfExists("game");
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
const players = ['white', 'black']
|
const players = ["white", "black"];
|
||||||
|
|
||||||
exports.up = knex => {
|
exports.up = (knex) => {
|
||||||
return knex.schema.createTable("move", table => {
|
return knex.schema.createTable("move", (table) => {
|
||||||
table.increments('id').primary();
|
table.increments("id").primary();
|
||||||
table.enu('player', players).notNullable();
|
table.enu("player", players).notNullable();
|
||||||
table.integer('point_x').notNullable();
|
table.integer("point_x").notNullable();
|
||||||
table.integer('point_y').notNullable();
|
table.integer("point_y").notNullable();
|
||||||
table.integer('number').notNullable();
|
table.integer("number").notNullable();
|
||||||
table.boolean('game_record').notNullable().default(true);
|
table.boolean("game_record").notNullable().default(true);
|
||||||
|
table.boolean("placement").notNullable().default(false);
|
||||||
|
|
||||||
table.integer('game').references('id').inTable('game').notNullable();
|
table.integer("game").references("id").inTable("game").notNullable();
|
||||||
table.integer('prior_move').references('id').inTable('move');
|
table.integer("prior_move").references("id").inTable("move");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.down = knex => knex.schema.dropTableIfExists("move");
|
exports.down = (knex) => knex.schema.dropTableIfExists("move");
|
||||||
|
|
|
@ -1,48 +1,97 @@
|
||||||
const knex = require('../db');
|
const knex = require("../db");
|
||||||
|
|
||||||
const gameDetailSelect = [
|
const gameDetailSelect = [
|
||||||
'game.id', 'application', 'application_version',
|
"game.id",
|
||||||
'board_size', 'komi', 'handicap', 'open', 'win_type',
|
"application",
|
||||||
'player_black', 'player_black_rank', 'player_white', 'player_white_rank',
|
"application_version",
|
||||||
'captures_black', 'captures_white', 'score', 'win_type',
|
"board_size",
|
||||||
'description', 'event', 'round', 'name', 'room'
|
"komi",
|
||||||
]
|
"handicap",
|
||||||
|
"open",
|
||||||
|
"win_type",
|
||||||
|
"player_black",
|
||||||
|
"player_black_rank",
|
||||||
|
"player_white",
|
||||||
|
"player_white_rank",
|
||||||
|
"captures_black",
|
||||||
|
"captures_white",
|
||||||
|
"score",
|
||||||
|
"win_type",
|
||||||
|
"description",
|
||||||
|
"event",
|
||||||
|
"round",
|
||||||
|
"name",
|
||||||
|
"room",
|
||||||
|
];
|
||||||
|
|
||||||
const timeSettingSelect = [
|
const timeSettingSelect = [
|
||||||
'main_time', 'time_period', 'period_length', 'overtime', 'overtime_period', 'overtime_length'
|
"main_time",
|
||||||
]
|
"time_period",
|
||||||
|
"period_length",
|
||||||
|
"overtime",
|
||||||
|
"overtime_period",
|
||||||
|
"overtime_length",
|
||||||
|
];
|
||||||
|
|
||||||
const gameOverviewSelect = [
|
const gameOverviewSelect = [
|
||||||
'id', 'board_size', 'komi', 'handicap', 'open', 'win_type',
|
"id",
|
||||||
'player_black', 'player_black_rank', 'player_white', 'player_white_rank'
|
"board_size",
|
||||||
]
|
"komi",
|
||||||
|
"handicap",
|
||||||
|
"open",
|
||||||
|
"win_type",
|
||||||
|
"player_black",
|
||||||
|
"player_black_rank",
|
||||||
|
"player_white",
|
||||||
|
"player_white_rank",
|
||||||
|
];
|
||||||
|
|
||||||
const findGameById = async function (gameId) {
|
const findGameById = async function (gameId) {
|
||||||
const selection = gameDetailSelect.concat(timeSettingSelect);
|
const selection = gameDetailSelect.concat(timeSettingSelect);
|
||||||
|
|
||||||
const game = await knex
|
const game = await knex
|
||||||
.from('game')
|
.from("game")
|
||||||
.select(selection)
|
.select(selection)
|
||||||
.where({'game.id': gameId})
|
.where({ "game.id": gameId })
|
||||||
.leftJoin('time_setting', function() { this.on('time_setting.id', '=', 'game.time_setting')})
|
.leftJoin("time_setting", function () {
|
||||||
|
this.on("time_setting.id", "=", "game.time_setting");
|
||||||
|
});
|
||||||
|
|
||||||
return game[0];
|
return game[0];
|
||||||
}
|
};
|
||||||
|
|
||||||
const findGameByRoom = async (roomId) => {
|
const findGameByRoom = async (roomId) => {
|
||||||
const games = await knex('game')
|
const games = await knex("game")
|
||||||
.where({'room': roomId})
|
.where({ room: roomId })
|
||||||
.select(gameOverviewSelect);
|
.select(gameOverviewSelect);
|
||||||
|
|
||||||
return games;
|
return games;
|
||||||
}
|
};
|
||||||
|
|
||||||
const insertGame = async (game) => {
|
const insertGame = async (game) => {};
|
||||||
|
|
||||||
|
const endGame = async ({ id, winType, score, bCaptures, wCaptures }) => {
|
||||||
|
try {
|
||||||
|
const game = await knex
|
||||||
|
.from("game")
|
||||||
|
.returning(gameDetailSelect)
|
||||||
|
.where({ id: id })
|
||||||
|
.update({
|
||||||
|
win_type: winType,
|
||||||
|
score: score,
|
||||||
|
captures_black: bCaptures,
|
||||||
|
captures_white: wCaptures,
|
||||||
|
open: false,
|
||||||
|
});
|
||||||
|
return game;
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
findGameById,
|
findGameById,
|
||||||
findGameByRoom,
|
findGameByRoom,
|
||||||
insertGame
|
insertGame,
|
||||||
}
|
endGame,
|
||||||
|
};
|
||||||
|
|
|
@ -1,11 +1,47 @@
|
||||||
const knex = require('../db');
|
const knex = require("../db");
|
||||||
|
|
||||||
const findGameRecord = async (gameId) => {
|
const findGameRecord = async (gameId) => {
|
||||||
return await knex('move')
|
return await knex("move")
|
||||||
.where({'game': gameId, 'game_record': true})
|
.where({ game: gameId, game_record: true })
|
||||||
.select('*');
|
.select("player", "point_x", "point_y", "number", "prior_move", "placement")
|
||||||
|
.orderBy("number")
|
||||||
|
.then((record) =>
|
||||||
|
record.map(({ player, point_x, point_y }) => ({
|
||||||
|
player,
|
||||||
|
pos: { x: point_x, y: point_y },
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
// .then(res => res)
|
||||||
|
};
|
||||||
|
|
||||||
|
// id: 1, player: 'black', point_x: 3, point_y: 3, number: 1, game_record: true, game: 1, prior_move: null
|
||||||
|
|
||||||
|
const addMove = async ({ gameId, player, x, y, gameRecord, priorMove }) => {
|
||||||
|
// ! priorMove must be FK not move number
|
||||||
|
const number = priorMove + 1;
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await knex("move")
|
||||||
|
.returning("*")
|
||||||
|
.insert({
|
||||||
|
game: gameId,
|
||||||
|
player,
|
||||||
|
point_x: x,
|
||||||
|
point_y: y,
|
||||||
|
number,
|
||||||
|
game_record: gameRecord,
|
||||||
|
prior_move: priorMove,
|
||||||
|
})
|
||||||
|
.then((res) => res);
|
||||||
|
} catch (e) {
|
||||||
|
result = e;
|
||||||
|
} finally {
|
||||||
|
console.log(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
findGameRecord
|
findGameRecord,
|
||||||
}
|
addMove,
|
||||||
|
};
|
||||||
|
|
|
@ -5,8 +5,8 @@ exports.seed = function(knex) {
|
||||||
.then(function () {
|
.then(function () {
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
return knex('room').insert([
|
return knex('room').insert([
|
||||||
{id: 1, name: 'main', description: 'A general place to play Go'},
|
{name: 'main', description: 'A general place to play Go'},
|
||||||
{id: 2, name: 'private', description: 'A private place to play Go', private: true},
|
{name: 'private', description: 'A private place to play Go', private: true},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ exports.seed = function(knex) {
|
||||||
.then(function () {
|
.then(function () {
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
return knex('time_setting').insert([
|
return knex('time_setting').insert([
|
||||||
{id: 1, main_time: 'untimed', time_period: 1, period_length: 0, overtime: 'untimed', overtime_period: 0, overtime_length: 0},
|
{main_time: 'untimed', time_period: 1, period_length: 0, overtime: 'untimed', overtime_period: 0, overtime_length: 0},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -10,9 +10,9 @@ exports.seed = async function(knex) {
|
||||||
.then(async function () {
|
.then(async function () {
|
||||||
const hashedPassword = await hashPassword(password);
|
const hashedPassword = await hashPassword(password);
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
return knex('user').insert([
|
return knex('user').returning('*').insert([
|
||||||
{id: 2, username: 'user-one', email: email, password: hashedPassword, admin: true},
|
{username: 'user-one', email: email, password: hashedPassword, admin: true},
|
||||||
{id: 3, username: 'user-two', email: `2${email}`, password: hashedPassword, admin: true},
|
{username: 'user-two', email: `2${email}`, password: hashedPassword, admin: true},
|
||||||
]);
|
]).then(entries => console.log({success: 'user', entries}));
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -1,33 +1,72 @@
|
||||||
|
exports.seed = async function (knex) {
|
||||||
exports.seed = function(knex) {
|
|
||||||
// Deletes ALL existing entries
|
// Deletes ALL existing entries
|
||||||
return knex('game').del()
|
return await knex("game")
|
||||||
.then(function () {
|
.del()
|
||||||
|
.then(async function () {
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
return knex('game').insert([
|
await knex("user")
|
||||||
|
.select("id")
|
||||||
|
.orderBy("id")
|
||||||
|
.whereIn("username", ["user-one", "user-two"])
|
||||||
|
.then(async ([userOne, userTwo]) => {
|
||||||
|
const res = await knex("room")
|
||||||
|
.select("id")
|
||||||
|
.where({ name: "main" })
|
||||||
|
.then(([room]) => {
|
||||||
|
console.log("inserting");
|
||||||
|
return knex("game")
|
||||||
|
.insert(
|
||||||
|
[
|
||||||
{
|
{
|
||||||
id: 1, date: new Date(),
|
date: new Date(),
|
||||||
application: 'node-go', application_version: '0.1.0',
|
application: "node-go",
|
||||||
player_black: 'user-one', player_white: 'user-two',
|
application_version: "0.1.0",
|
||||||
player_black_rank: 'UR', player_white_rank: 'UR',
|
player_black: "user-one",
|
||||||
user_black: 2, user_white: 3,
|
player_white: "user-two",
|
||||||
room: 1, time_setting: 1, open: false
|
player_black_rank: "UR",
|
||||||
|
player_white_rank: "UR",
|
||||||
|
user_black: userOne.id,
|
||||||
|
user_white: userTwo.id,
|
||||||
|
room: room.id,
|
||||||
|
open: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2, date: new Date(),
|
date: new Date(),
|
||||||
application: 'node-go', application_version: '0.1.0',
|
application: "node-go",
|
||||||
player_black: 'user-one', player_black_rank: 'UR',
|
application_version: "0.1.0",
|
||||||
user_black: 2,
|
player_black: "user-one",
|
||||||
room: 1, time_setting: 1, open: true
|
player_black_rank: "UR",
|
||||||
|
user_black: userTwo.id,
|
||||||
|
room: room.id,
|
||||||
|
open: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3, date: new Date('1971-05-06'),
|
date: new Date("1971-05-06"),
|
||||||
application: 'node-go', application_version: '0.1.0',
|
application: "node-go",
|
||||||
player_black: 'Ishida Yoshio', player_black_rank: 'D7',
|
application_version: "0.1.0",
|
||||||
player_white: 'Rin Kaiho', player_white_rank: 'D9',
|
player_black: "Ishida Yoshio",
|
||||||
room: 1, time_setting: 1, open: false,
|
player_black_rank: "D7",
|
||||||
event: '', round: 2, win_type: 'B+', score: 1.5
|
player_white: "Rin Kaiho",
|
||||||
}
|
player_white_rank: "D9",
|
||||||
]);
|
room: room.id,
|
||||||
|
open: false,
|
||||||
|
event: "",
|
||||||
|
round: 2,
|
||||||
|
win_type: "B+",
|
||||||
|
score: 1.5,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
["*"]
|
||||||
|
)
|
||||||
|
.then((res) => res)
|
||||||
|
.catch((e) => {
|
||||||
|
console.log("error");
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((entries) => {
|
||||||
|
console.log({ success: "game", entries });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -1,11 +1,27 @@
|
||||||
|
|
||||||
exports.seed = function(knex) {
|
exports.seed = async function(knex) {
|
||||||
// Deletes ALL existing entries
|
// Deletes ALL existing entries
|
||||||
return knex('message').del()
|
return await knex('message').del()
|
||||||
.then(function () {
|
.then(async function () {
|
||||||
// Inserts seed entries
|
// Inserts seed entries
|
||||||
return knex('message').insert([
|
await knex('room')
|
||||||
{id: 1, content: 'Hey! Welcome to the general room!', room: 1, user: 2}
|
.where({name: 'main'})
|
||||||
]);
|
.then(async ([room]) => await knex('user')
|
||||||
});
|
.where({username: 'user-two'})
|
||||||
|
.then(async ([user]) => {
|
||||||
|
const res = await knex('message')
|
||||||
|
.returning('*')
|
||||||
|
.insert(
|
||||||
|
[
|
||||||
|
{content: 'Hey! Welcome to the general room!', room: room.id, user: user.id}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.then(entries => {console.log({success: 'message', entries}); return res;})
|
||||||
|
.catch(e => e)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {})
|
||||||
|
).then(() => {})
|
||||||
|
}).then(() => {})
|
||||||
};
|
};
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
||||||
const {check, sanitize, validationResult} = require('express-validator');
|
const {check, body, validationResult} = require('express-validator');
|
||||||
|
|
||||||
const signupValidationRules = () => {
|
const signupValidationRules = () => {
|
||||||
return [
|
return [
|
||||||
|
@ -7,7 +7,7 @@ const signupValidationRules = () => {
|
||||||
check('password', 'invalid password').isString().isLength({min: 8}),
|
check('password', 'invalid password').isString().isLength({min: 8}),
|
||||||
check('confirmPassword', 'invalid password').isString()
|
check('confirmPassword', 'invalid password').isString()
|
||||||
.custom((confirmPassword, { req }) => confirmPassword === req.body.password),
|
.custom((confirmPassword, { req }) => confirmPassword === req.body.password),
|
||||||
sanitize('username').escape()
|
body('username').escape()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
458
packages/server/package-lock.json
generated
458
packages/server/package-lock.json
generated
|
@ -24,9 +24,9 @@
|
||||||
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "3.0.0",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
@ -176,12 +176,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bcrypt": {
|
"bcrypt": {
|
||||||
"version": "3.0.7",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-4.0.1.tgz",
|
||||||
"integrity": "sha512-K5UglF9VQvBMHl/1elNyyFvAfOY9Bj+rpKrCSR9sFwcW8FywAYJSRwTURNej5TaAK2TEJkcJ6r6lh1YPmspx5Q==",
|
"integrity": "sha512-hSIZHkUxIDS5zA2o00Kf2O5RfVbQ888n54xQoF/eIaquU4uaLxK8vhhBdktd0B3n2MjkcAWzv4mnhogykBKOUQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"nan": "2.14.0",
|
"node-addon-api": "^2.0.0",
|
||||||
"node-pre-gyp": "0.13.0"
|
"node-pre-gyp": "0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"better-assert": {
|
"better-assert": {
|
||||||
|
@ -197,11 +197,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||||
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
||||||
},
|
},
|
||||||
"bluebird": {
|
|
||||||
"version": "3.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
|
||||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
|
||||||
},
|
|
||||||
"body-parser": {
|
"body-parser": {
|
||||||
"version": "1.18.3",
|
"version": "1.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
|
||||||
|
@ -217,6 +212,16 @@
|
||||||
"qs": "6.5.2",
|
"qs": "6.5.2",
|
||||||
"raw-body": "2.3.3",
|
"raw-body": "2.3.3",
|
||||||
"type-is": "~1.6.16"
|
"type-is": "~1.6.16"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||||
|
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
|
@ -292,9 +297,9 @@
|
||||||
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
||||||
},
|
},
|
||||||
"chownr": {
|
"chownr": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||||
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||||
},
|
},
|
||||||
"class-utils": {
|
"class-utils": {
|
||||||
"version": "0.3.6",
|
"version": "0.3.6",
|
||||||
|
@ -337,9 +342,9 @@
|
||||||
"integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg=="
|
"integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg=="
|
||||||
},
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "4.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
|
||||||
"integrity": "sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw=="
|
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="
|
||||||
},
|
},
|
||||||
"component-bind": {
|
"component-bind": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -377,16 +382,16 @@
|
||||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
||||||
},
|
},
|
||||||
"cookie": {
|
"cookie": {
|
||||||
"version": "0.3.1",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
},
|
},
|
||||||
"cookie-parser": {
|
"cookie-parser": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
|
||||||
"integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==",
|
"integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"cookie": "0.3.1",
|
"cookie": "0.4.0",
|
||||||
"cookie-signature": "1.0.6"
|
"cookie-signature": "1.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -420,6 +425,13 @@
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decode-uri-component": {
|
"decode-uri-component": {
|
||||||
|
@ -518,9 +530,9 @@
|
||||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
},
|
},
|
||||||
"engine.io": {
|
"engine.io": {
|
||||||
"version": "3.4.0",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz",
|
||||||
"integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==",
|
"integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"accepts": "~1.3.4",
|
"accepts": "~1.3.4",
|
||||||
"base64id": "2.0.0",
|
"base64id": "2.0.0",
|
||||||
|
@ -530,6 +542,11 @@
|
||||||
"ws": "^7.1.2"
|
"ws": "^7.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
|
@ -537,18 +554,13 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"engine.io-client": {
|
"engine.io-client": {
|
||||||
"version": "3.4.0",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.1.tgz",
|
||||||
"integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==",
|
"integrity": "sha512-RJNmA+A9Js+8Aoq815xpGAsgWH1VoSYM//2VgIiu9lNOaHFfLpTjH4tOzktBpjIs5lvOfiNY1dwf+NuU6D38Mw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"component-emitter": "1.2.1",
|
"component-emitter": "1.2.1",
|
||||||
"component-inherit": "0.0.3",
|
"component-inherit": "0.0.3",
|
||||||
|
@ -576,11 +588,6 @@
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "6.1.4",
|
"version": "6.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
|
||||||
|
@ -608,6 +615,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||||
},
|
},
|
||||||
|
"esm": {
|
||||||
|
"version": "3.2.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
|
||||||
|
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA=="
|
||||||
|
},
|
||||||
"etag": {
|
"etag": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
@ -688,15 +700,22 @@
|
||||||
"type-is": "~1.6.16",
|
"type-is": "~1.6.16",
|
||||||
"utils-merge": "1.0.1",
|
"utils-merge": "1.0.1",
|
||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"express-validator": {
|
"express-validator": {
|
||||||
"version": "6.3.1",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.4.1.tgz",
|
||||||
"integrity": "sha512-YQHQKP/zlUTN6d38uWwXgK3At5phK6R24pOB/ImWisMUz/U/1AC3ZXMgiZYhtH4ViYJ6UAiV0/nj8s1Qs3kmvw==",
|
"integrity": "sha512-9lVLCv9n735j6LjZwrINKhfv30ek1U4LVy/2jU1j4QotQ8A72Saw63wTu8/zXGZajP3scqf46PJxqquGF340Bg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"validator": "^11.1.0"
|
"validator": "^12.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
|
@ -902,39 +921,6 @@
|
||||||
"string-width": "^1.0.1",
|
"string-width": "^1.0.1",
|
||||||
"strip-ansi": "^3.0.1",
|
"strip-ansi": "^3.0.1",
|
||||||
"wide-align": "^1.1.0"
|
"wide-align": "^1.1.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
|
||||||
},
|
|
||||||
"is-fullwidth-code-point": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
|
||||||
"requires": {
|
|
||||||
"number-is-nan": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string-width": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
|
||||||
"requires": {
|
|
||||||
"code-point-at": "^1.0.0",
|
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
|
||||||
"strip-ansi": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"strip-ansi": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
|
||||||
"requires": {
|
|
||||||
"ansi-regex": "^2.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"get-value": {
|
"get-value": {
|
||||||
|
@ -948,9 +934,9 @@
|
||||||
"integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA=="
|
"integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA=="
|
||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
|
@ -1053,12 +1039,19 @@
|
||||||
"inherits": "2.0.3",
|
"inherits": "2.0.3",
|
||||||
"setprototypeof": "1.1.0",
|
"setprototypeof": "1.1.0",
|
||||||
"statuses": ">= 1.4.0 < 2"
|
"statuses": ">= 1.4.0 < 2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.23",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
}
|
}
|
||||||
|
@ -1086,9 +1079,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
@ -1101,9 +1094,9 @@
|
||||||
"integrity": "sha512-e0/LknJ8wpMMhTiWcjivB+ESwIuvHnBSlBbmP/pSb8CQJldoj1p2qv7xGZ/+BtbTziYRFSz8OsvdbiX45LtYQA=="
|
"integrity": "sha512-e0/LknJ8wpMMhTiWcjivB+ESwIuvHnBSlBbmP/pSb8CQJldoj1p2qv7xGZ/+BtbTziYRFSz8OsvdbiX45LtYQA=="
|
||||||
},
|
},
|
||||||
"ipaddr.js": {
|
"ipaddr.js": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||||
},
|
},
|
||||||
"is-absolute": {
|
"is-absolute": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -1183,9 +1176,12 @@
|
||||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
|
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
|
||||||
},
|
},
|
||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "2.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
|
"requires": {
|
||||||
|
"number-is-nan": "^1.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"is-glob": {
|
"is-glob": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
@ -1274,11 +1270,6 @@
|
||||||
"semver": "^5.6.0"
|
"semver": "^5.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.7.1",
|
"version": "5.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
@ -1306,29 +1297,29 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"kind-of": {
|
"kind-of": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
|
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
|
||||||
},
|
},
|
||||||
"knex": {
|
"knex": {
|
||||||
"version": "0.20.7",
|
"version": "0.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/knex/-/knex-0.20.7.tgz",
|
"resolved": "https://registry.npmjs.org/knex/-/knex-0.21.1.tgz",
|
||||||
"integrity": "sha512-Yu5WfQqK9JSkxMAWnCdeWbsjTKTzsjMYjqmUowOQnyLMs4RspVehqXCVjg+aTtcVrTtIZ3WvoeE00OzFsZKJow==",
|
"integrity": "sha512-uWszXC2DPaLn/YznGT9wFTWUG9+kqbL4DMz+hCH789GLcLuYzq8werHPDKBJxtKvxrW/S1XIXgrTWdMypiVvsw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bluebird": "^3.7.2",
|
|
||||||
"colorette": "1.1.0",
|
"colorette": "1.1.0",
|
||||||
"commander": "^4.1.0",
|
"commander": "^5.1.0",
|
||||||
"debug": "4.1.1",
|
"debug": "4.1.1",
|
||||||
|
"esm": "^3.2.25",
|
||||||
"getopts": "2.2.5",
|
"getopts": "2.2.5",
|
||||||
"inherits": "~2.0.4",
|
"inherits": "~2.0.4",
|
||||||
"interpret": "^2.0.0",
|
"interpret": "^2.0.0",
|
||||||
"liftoff": "3.1.0",
|
"liftoff": "3.1.0",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^1.0.4",
|
||||||
"pg-connection-string": "2.1.0",
|
"pg-connection-string": "2.2.0",
|
||||||
"tarn": "^2.0.0",
|
"tarn": "^3.0.0",
|
||||||
"tildify": "2.0.0",
|
"tildify": "2.0.0",
|
||||||
"uuid": "^3.3.3",
|
"uuid": "^7.0.3",
|
||||||
"v8flags": "^3.1.3"
|
"v8flags": "^3.1.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1340,20 +1331,15 @@
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inherits": {
|
"mkdirp": {
|
||||||
"version": "2.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
},
|
||||||
"pg-connection-string": {
|
"pg-connection-string": {
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.0.tgz",
|
||||||
"integrity": "sha512-bhlV7Eq09JrRIvo1eKngpwuqKtJnNhZdpdOlvrPrA4dxqXPjxSrbNrfnIDmTpwMyRszrcV4kU5ZA4mMsQUrjdg=="
|
"integrity": "sha512-xB/+wxcpFipUZOQcSzcgkjcNOosGhEoPSjz06jC89lv1dj7mc9bZv6wLVy8M2fVjP0a/xN0N988YDq1L0FhK3A=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1373,9 +1359,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.19",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
|
||||||
},
|
},
|
||||||
"lodash.includes": {
|
"lodash.includes": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
|
@ -1474,16 +1460,16 @@
|
||||||
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
|
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
|
||||||
},
|
},
|
||||||
"mime-db": {
|
"mime-db": {
|
||||||
"version": "1.43.0",
|
"version": "1.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||||
},
|
},
|
||||||
"mime-types": {
|
"mime-types": {
|
||||||
"version": "2.1.26",
|
"version": "2.1.27",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "1.43.0"
|
"mime-db": "1.44.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
|
@ -1495,9 +1481,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
|
@ -1536,11 +1522,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "^1.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"morgan": {
|
"morgan": {
|
||||||
|
@ -1556,14 +1542,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
|
||||||
"nan": {
|
|
||||||
"version": "2.14.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
|
||||||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
|
|
||||||
},
|
},
|
||||||
"nanomatch": {
|
"nanomatch": {
|
||||||
"version": "1.2.13",
|
"version": "1.2.13",
|
||||||
|
@ -1584,9 +1565,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"needle": {
|
"needle": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/needle/-/needle-2.4.1.tgz",
|
||||||
"integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==",
|
"integrity": "sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^3.2.6",
|
"debug": "^3.2.6",
|
||||||
"iconv-lite": "^0.4.4",
|
"iconv-lite": "^0.4.4",
|
||||||
|
@ -1600,11 +1581,6 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1613,10 +1589,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||||
},
|
},
|
||||||
|
"node-addon-api": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
|
||||||
|
},
|
||||||
"node-pre-gyp": {
|
"node-pre-gyp": {
|
||||||
"version": "0.13.0",
|
"version": "0.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz",
|
||||||
"integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==",
|
"integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"detect-libc": "^1.0.2",
|
"detect-libc": "^1.0.2",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
|
@ -1627,7 +1608,7 @@
|
||||||
"rc": "^1.2.7",
|
"rc": "^1.2.7",
|
||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"tar": "^4"
|
"tar": "^4.4.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": {
|
"semver": {
|
||||||
|
@ -1638,9 +1619,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nopt": {
|
"nopt": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||||
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
|
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abbrev": "1",
|
"abbrev": "1",
|
||||||
"osenv": "^0.1.4"
|
"osenv": "^0.1.4"
|
||||||
|
@ -1660,12 +1641,13 @@
|
||||||
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
|
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
|
||||||
},
|
},
|
||||||
"npm-packlist": {
|
"npm-packlist": {
|
||||||
"version": "1.4.7",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
|
||||||
"integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==",
|
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ignore-walk": "^3.0.1",
|
"ignore-walk": "^3.0.1",
|
||||||
"npm-bundled": "^1.0.1"
|
"npm-bundled": "^1.0.1",
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmlog": {
|
"npmlog": {
|
||||||
|
@ -1873,39 +1855,39 @@
|
||||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||||
},
|
},
|
||||||
"pg": {
|
"pg": {
|
||||||
"version": "7.17.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-7.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.1.0.tgz",
|
||||||
"integrity": "sha512-70Q4ZzIdPgwMPb3zUIzAUwigNJ4v5vsWdMED6OzXMfOECeYTvTm7iSC3FpKizu/R1BHL8Do3bLs6ltGfOTAnqg==",
|
"integrity": "sha512-Jp+XSNTGYDztc2FgIbmBXeeYMR7kKjfgnl3R+ioO6rkcxDmaea+YPp/gaxe13PBnJAFYyEGl0ixpwPm2gb6eUw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"buffer-writer": "2.0.0",
|
"buffer-writer": "2.0.0",
|
||||||
"packet-reader": "1.0.0",
|
"packet-reader": "1.0.0",
|
||||||
"pg-connection-string": "0.1.3",
|
"pg-connection-string": "^2.2.2",
|
||||||
"pg-packet-stream": "^1.1.0",
|
"pg-pool": "^3.2.0",
|
||||||
"pg-pool": "^2.0.9",
|
"pg-protocol": "^1.2.2",
|
||||||
"pg-types": "^2.1.0",
|
"pg-types": "^2.1.0",
|
||||||
"pgpass": "1.x",
|
"pgpass": "1.x",
|
||||||
"semver": "4.3.2"
|
"semver": "4.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pg-connection-string": {
|
"pg-connection-string": {
|
||||||
"version": "0.1.3",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.2.tgz",
|
||||||
"integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc="
|
"integrity": "sha512-+hel4DGuSZCjCZwglAuyi+XlodHnKmrbyTw0hVWlmGN2o4AfJDkDo5obAFzblS5M5PFBMx0uDt5Y1QjlNC+tqg=="
|
||||||
},
|
},
|
||||||
"pg-int8": {
|
"pg-int8": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
|
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
|
||||||
},
|
},
|
||||||
"pg-packet-stream": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-kRBH0tDIW/8lfnnOyTwKD23ygJ/kexQVXZs7gEyBljw4FYqimZFxnMMx50ndZ8In77QgfGuItS5LLclC2TtjYg=="
|
|
||||||
},
|
|
||||||
"pg-pool": {
|
"pg-pool": {
|
||||||
"version": "2.0.9",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.0.tgz",
|
||||||
"integrity": "sha512-gNiuIEKNCT3OnudQM2kvgSnXsLkSpd6mS/fRnqs6ANtrke6j8OY5l9mnAryf1kgwJMWLg0C1N1cYTZG1xmEYHQ=="
|
"integrity": "sha512-7BLwDNDEfPFjE9vmZLcJPLFwuDAVGZ5lIZo2MeQfwYG7EPGfdNVis/dz6obI/yKqvQIx2sf6QBKXMLB+y/ftgA=="
|
||||||
|
},
|
||||||
|
"pg-protocol": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-r8hGxHOk3ccMjjmhFJ/QOSVW5A+PP84TeRlEwB/cQ9Zu+bvtZg8Z59Cx3AMfVQc9S0Z+EG+HKhicF1W1GN5Eqg=="
|
||||||
},
|
},
|
||||||
"pg-types": {
|
"pg-types": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
|
@ -1943,9 +1925,9 @@
|
||||||
"integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
|
"integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
|
||||||
},
|
},
|
||||||
"postgres-date": {
|
"postgres-date": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.5.tgz",
|
||||||
"integrity": "sha512-bESRvKVuTrjoBluEcpv2346+6kgB7UlnqWZsnbnCccTNq/pqfj1j6oBaN5+b/NrDXepYUT/HKadqv3iS9lJuVA=="
|
"integrity": "sha512-pdau6GRPERdAYUQwkBnGKxEfPyhVZXG/JiS44iZWiNdSOWE09N2lUgN6yshuq6fVSon4Pm0VMXd1srUUkLe9iA=="
|
||||||
},
|
},
|
||||||
"postgres-interval": {
|
"postgres-interval": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
@ -1961,12 +1943,12 @@
|
||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
},
|
},
|
||||||
"proxy-addr": {
|
"proxy-addr": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||||
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
|
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"forwarded": "~0.1.2",
|
"forwarded": "~0.1.2",
|
||||||
"ipaddr.js": "1.9.0"
|
"ipaddr.js": "1.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
|
@ -1988,6 +1970,16 @@
|
||||||
"http-errors": "1.6.3",
|
"http-errors": "1.6.3",
|
||||||
"iconv-lite": "0.4.23",
|
"iconv-lite": "0.4.23",
|
||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||||
|
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rc": {
|
"rc": {
|
||||||
|
@ -1999,13 +1991,6 @@
|
||||||
"ini": "~1.3.0",
|
"ini": "~1.3.0",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"strip-json-comments": "~2.0.1"
|
"strip-json-comments": "~2.0.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"minimist": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
|
@ -2050,9 +2035,9 @@
|
||||||
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
|
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
|
||||||
},
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.14.2",
|
"version": "1.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
|
||||||
"integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==",
|
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"path-parse": "^1.0.6"
|
"path-parse": "^1.0.6"
|
||||||
}
|
}
|
||||||
|
@ -2130,6 +2115,13 @@
|
||||||
"on-finished": "~2.3.0",
|
"on-finished": "~2.3.0",
|
||||||
"range-parser": "~1.2.0",
|
"range-parser": "~1.2.0",
|
||||||
"statuses": "~1.4.0"
|
"statuses": "~1.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"serve-static": {
|
"serve-static": {
|
||||||
|
@ -2175,9 +2167,9 @@
|
||||||
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
||||||
},
|
},
|
||||||
"signal-exit": {
|
"signal-exit": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
|
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||||
},
|
},
|
||||||
"snapdragon": {
|
"snapdragon": {
|
||||||
"version": "0.8.2",
|
"version": "0.8.2",
|
||||||
|
@ -2296,11 +2288,6 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2348,11 +2335,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||||
},
|
},
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
},
|
|
||||||
"socket.io-parser": {
|
"socket.io-parser": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
|
||||||
|
@ -2407,11 +2389,6 @@
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2478,12 +2455,13 @@
|
||||||
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
|
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
|
||||||
},
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "2.1.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-fullwidth-code-point": "^2.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"strip-ansi": "^4.0.0"
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
"strip-ansi": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string_decoder": {
|
"string_decoder": {
|
||||||
|
@ -2495,11 +2473,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "4.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^3.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"strip-json-comments": {
|
"strip-json-comments": {
|
||||||
|
@ -2522,9 +2500,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tarn": {
|
"tarn": {
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/tarn/-/tarn-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.0.tgz",
|
||||||
"integrity": "sha512-7rNMCZd3s9bhQh47ksAQd92ADFcJUjjbyOvyFjNLwTPpGieFHMC84S+LOzw0fx1uh6hnDz/19r8CPMnIjJlMMA=="
|
"integrity": "sha512-PKUnlDFODZueoA8owLehl8vLcgtA8u4dRuVbZc92tspDYZixjJL6TqYOmryf/PfP/EBX+2rgNcrj96NO+RPkdQ=="
|
||||||
},
|
},
|
||||||
"through": {
|
"through": {
|
||||||
"version": "2.3.8",
|
"version": "2.3.8",
|
||||||
|
@ -2666,9 +2644,9 @@
|
||||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "3.3.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
||||||
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
|
"integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
|
||||||
},
|
},
|
||||||
"v8flags": {
|
"v8flags": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
|
@ -2679,9 +2657,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"version": "11.1.0",
|
"version": "12.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-11.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz",
|
||||||
"integrity": "sha512-qiQ5ktdO7CD6C/5/mYV4jku/7qnqzjrxb3C/Q5wR3vGGinHTgJZN/TdFT3ZX4vXhX2R1PXx42fB1cn5W+uJ4lg=="
|
"integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ=="
|
||||||
},
|
},
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
|
@ -2710,9 +2688,9 @@
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.2.1",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz",
|
||||||
"integrity": "sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A=="
|
"integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w=="
|
||||||
},
|
},
|
||||||
"xmlhttprequest-ssl": {
|
"xmlhttprequest-ssl": {
|
||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"reset-db": "./node_modules/.bin/knex migrate:rollback true && npm run migrate && npm run seed"
|
"reset-db": "./node_modules/.bin/knex migrate:rollback true && npm run migrate && npm run seed"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^3.0.7",
|
"bcrypt": "^4.0.1",
|
||||||
"cookie-parser": "~1.4.4",
|
"cookie-parser": "~1.4.4",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"debug": "~2.6.9",
|
"debug": "~2.6.9",
|
||||||
|
@ -22,9 +22,9 @@
|
||||||
"express-validator": "^6.3.1",
|
"express-validator": "^6.3.1",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "~1.6.3",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"knex": "^0.20.7",
|
"knex": "^0.21.1",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
"pg": "^7.17.0",
|
"pg": "^8.1.0",
|
||||||
"socket.io": "^2.3.0"
|
"socket.io": "^2.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
const express = require('express');
|
const express = require("express");
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const app = require('../server');
|
const app = require("../server");
|
||||||
const authController = require('../controllers/auth');
|
const authController = require("../controllers/auth");
|
||||||
const { signupValidationRules, loginValidationRules, validate } = require('../middleware/userValidator');
|
const {
|
||||||
|
signupValidationRules,
|
||||||
|
loginValidationRules,
|
||||||
|
validate,
|
||||||
|
} = require("../middleware/userValidator");
|
||||||
|
|
||||||
router.post('/signup', signupValidationRules(), validate, authController.signup);
|
router.post(
|
||||||
router.post('/login', loginValidationRules(), validate, authController.login);
|
"/signup",
|
||||||
|
signupValidationRules(),
|
||||||
|
validate,
|
||||||
|
authController.signup
|
||||||
|
);
|
||||||
|
router.post("/login", loginValidationRules(), validate, authController.login);
|
||||||
|
router.post("/guest", authController.guest);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
const createError = require('http-errors');
|
const createError = require("http-errors");
|
||||||
const express = require('express');
|
const express = require("express");
|
||||||
|
|
||||||
const cors = require('cors');
|
const cors = require("cors");
|
||||||
|
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const cookieParser = require('cookie-parser');
|
const cookieParser = require("cookie-parser");
|
||||||
const logger = require('morgan');
|
const logger = require("morgan");
|
||||||
|
|
||||||
const db = require('./data/db');
|
const db = require("./data/db");
|
||||||
|
|
||||||
const dotenv = require('dotenv');
|
const dotenv = require("dotenv");
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const indexRouter = require('./routes/index');
|
const indexRouter = require("./routes/index");
|
||||||
const usersRouter = require('./routes/users');
|
const usersRouter = require("./routes/users");
|
||||||
const authRouter = require('./routes/auth');
|
const authRouter = require("./routes/auth");
|
||||||
const apiRouter = require('./routes/api');
|
const apiRouter = require("./routes/api");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
@ -23,25 +23,25 @@ const allowedOrigin = process.env.REACT_ADDRESS;
|
||||||
const corsOptions = {
|
const corsOptions = {
|
||||||
origin: allowedOrigin,
|
origin: allowedOrigin,
|
||||||
credentials: true,
|
credentials: true,
|
||||||
methods: "GET,PUT,POST,DELETE"
|
methods: "GET,PUT,POST,DELETE",
|
||||||
}
|
};
|
||||||
|
|
||||||
app.options('*', cors(corsOptions));
|
app.options("*", cors(corsOptions));
|
||||||
app.use('*', cors(corsOptions));
|
app.use("*", cors(corsOptions));
|
||||||
|
|
||||||
// disable logging for tests
|
// disable logging for tests
|
||||||
if (process.env.NODE_ENV !== 'test') app.use(logger('dev'));
|
if (process.env.NODE_ENV !== "test") app.use(logger("dev"));
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.urlencoded({ extended: false }));
|
app.use(express.urlencoded({ extended: false }));
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(express.static(path.join(__dirname, 'public')));
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
app.use('/', indexRouter);
|
app.use("/", indexRouter);
|
||||||
app.use('/users', usersRouter);
|
app.use("/users", usersRouter);
|
||||||
app.use('/auth', authRouter);
|
app.use("/auth", authRouter);
|
||||||
// @auth
|
// @auth
|
||||||
app.use('/api/v1', apiRouter);
|
app.use("/api/v1", apiRouter);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
// catch 404 and forward to error handler
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
|
@ -52,11 +52,11 @@ app.use(function(req, res, next) {
|
||||||
app.use(function (err, req, res, next) {
|
app.use(function (err, req, res, next) {
|
||||||
// set locals, only providing error in development
|
// set locals, only providing error in development
|
||||||
res.locals.message = err.message;
|
res.locals.message = err.message;
|
||||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
res.locals.error = req.app.get("env") === "development" ? err : {};
|
||||||
|
|
||||||
// render the error page
|
// render the error page
|
||||||
res.status(err.status || 500);
|
res.status(err.status || 500);
|
||||||
res.send('error');
|
res.send("error");
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
422
packages/server/services/Game.v1.js
Normal file
422
packages/server/services/Game.v1.js
Normal file
|
@ -0,0 +1,422 @@
|
||||||
|
/*----- constants -----*/
|
||||||
|
const STONES_DATA = {
|
||||||
|
'-1': 'white',
|
||||||
|
'0': 'none',
|
||||||
|
'1': 'black',
|
||||||
|
'k': 'ko'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// index corresponds to difference in player rank
|
||||||
|
const KOMI_REC = {
|
||||||
|
'9': [
|
||||||
|
5.5, 2.5, -0.5, -3.5, -6.5, -9.5, 12.5, 15.5, 18.5, 21.5
|
||||||
|
],
|
||||||
|
'13': [
|
||||||
|
5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5
|
||||||
|
],
|
||||||
|
'19': [
|
||||||
|
7.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const HANDI_REC = {
|
||||||
|
'9': [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
],
|
||||||
|
'13': [
|
||||||
|
0, 0, 0, 2, 2, 3, 3, 4, 4, 5
|
||||||
|
],
|
||||||
|
'19': [
|
||||||
|
0, 0, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// index represents handicap placement for different board-sizes, eg handiPlace['9][1] = { (3, 3), (7, 7) }
|
||||||
|
// last array in each property also used for hoshi rendering
|
||||||
|
const HANDI_PLACE = {
|
||||||
|
'9' : [
|
||||||
|
0, 0,
|
||||||
|
[[ 7, 3 ], [ 3, 7 ] ],
|
||||||
|
[ [ 7, 7 ], [ 7, 3 ], [ 3, 7 ] ],
|
||||||
|
[ [ 3, 3 ], [ 7, 7 ], [ 3, 7 ], [ 7, 3 ] ]
|
||||||
|
],
|
||||||
|
'13' : [
|
||||||
|
0, 0,
|
||||||
|
[ [ 4, 10 ], [ 10, 4 ] ],
|
||||||
|
[ [ 10, 10 ], [ 4, 10 ], [ 10, 4] ],
|
||||||
|
[ [ 4, 4 ], [ 10, 10 ], [ 4, 10 ], [ 10, 4] ],
|
||||||
|
[ [ 7, 7 ], [ 4, 4 ], [ 10, 10 ], [ 4, 10 ], [ 10, 4] ],
|
||||||
|
[ [ 7, 4 ], [ 4, 7 ], [ 4, 4 ], [ 10, 10 ], [ 4, 10 ], [ 10, 4] ],
|
||||||
|
[ [ 7, 7 ], [ 7, 4 ], [ 4, 7 ], [ 4, 4 ], [ 10, 10 ], [ 4, 10 ], [ 10, 4] ],
|
||||||
|
[ [ 10, 7 ], [ 7, 4 ], [ 7, 10 ], [ 4, 7 ], [ 4, 4 ], [ 10, 10 ], [ 4, 10 ], [ 10, 4] ],
|
||||||
|
[ [ 7, 7 ], [ 10, 7 ], [ 7, 4 ], [ 7, 10 ], [ 4, 7 ], [ 4, 4 ], [ 10, 10 ], [ 4, 10 ], [ 10, 4] ],
|
||||||
|
],
|
||||||
|
'19' : [
|
||||||
|
0, 0,
|
||||||
|
[ [ 4, 16 ], [ 16, 4 ] ],
|
||||||
|
[ [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
|
||||||
|
[ [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
|
||||||
|
[ [ 10, 10 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
|
||||||
|
[ [ 10, 4 ], [ 4, 10 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
|
||||||
|
[ [ 10, 10 ], [ 10, 4 ], [ 4, 10 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
|
||||||
|
[ [ 16, 10 ], [ 10, 4 ], [ 10, 16 ], [ 4, 10 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
|
||||||
|
[ [ 10, 10 ], [ 16, 10 ], [ 10, 4 ], [ 10, 16 ], [ 4, 10 ], [ 4, 4 ], [ 16, 16 ], [ 4, 16 ], [ 16, 4] ],
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
class Game {
|
||||||
|
constructor(gameData, gameRecord) {
|
||||||
|
this.winner = gameData.winner || null,
|
||||||
|
this.turn = gameData.turn || 1, // turn logic depends on handicap stones
|
||||||
|
this.pass = gameData.pass || 0, // -1 represents state in which resignation has been submitted, not confirmed
|
||||||
|
this.komi = gameData.komi || 6.5, // komi depends on handicap stones + player rank
|
||||||
|
this.handicap = gameData.handicap || 0,
|
||||||
|
this.boardSize = gameData.boardSize || 19,
|
||||||
|
this.groups = {},
|
||||||
|
this.boardState = [],
|
||||||
|
this.gameRecord = gameRecord || [],
|
||||||
|
this.playerState = gameData.playerState || {
|
||||||
|
bCaptures: 0,
|
||||||
|
wCaptures: 0,
|
||||||
|
bScore: 0,
|
||||||
|
wScore: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initGame = () => {
|
||||||
|
this.winner = null;
|
||||||
|
this.pass = null;
|
||||||
|
this.turn = this.handicap ? -1 : 1;
|
||||||
|
|
||||||
|
this.initBoard();
|
||||||
|
return this.getBoardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
initBoard = () => {
|
||||||
|
let i = 0;
|
||||||
|
while (i < this.boardSize * this.boardSize) {
|
||||||
|
let point = new Point( Math.floor(i / this.boardSize) + 1, i % this.boardSize + 1, this)
|
||||||
|
this.boardState.push(point);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
this.initHandi();
|
||||||
|
}
|
||||||
|
|
||||||
|
initHandi = () => {
|
||||||
|
if (this.handicap < 2) return;
|
||||||
|
HANDI_PLACE[this.boardSize][this.handicap].forEach(pt => {
|
||||||
|
if (!pt) return;
|
||||||
|
let handi = this.findPointFromIdx(pt);
|
||||||
|
handi.stone = 1;
|
||||||
|
handi.joinGroup(this);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getBoardState = () => {
|
||||||
|
this.boardState.forEach(point => point.legal = checkLegal(point, this))
|
||||||
|
return this.boardState.reduce((boardState, point) => {
|
||||||
|
boardState[`${point.pos[0]}-${point.pos[1]}`] = point.legal || point.stone;
|
||||||
|
return boardState;
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
getMeta = () => {
|
||||||
|
return { winner: this.winner, turn: this.turn, pass: this.pass, playerState: this.playerState, gameRecord: this.gameRecord }
|
||||||
|
}
|
||||||
|
|
||||||
|
findPointFromIdx = (arr) => {
|
||||||
|
return this.boardState.find( point => point.pos[0] === arr[0] && point.pos[1] === arr[1] );
|
||||||
|
}
|
||||||
|
|
||||||
|
makeMove = (move) => {
|
||||||
|
const player = move.player === 'white' ? -1 : 1;
|
||||||
|
const point = this.findPointFromIdx([move.pos.x, move.pos.y])
|
||||||
|
if ( !checkLegal(point, this) ) throw Error('illegal move');
|
||||||
|
clearKo(this);
|
||||||
|
clearPass(this);
|
||||||
|
resolveCaptures(point, this);
|
||||||
|
point.stone = this.turn;
|
||||||
|
point.joinGroup(this);
|
||||||
|
clearCaptures(this);
|
||||||
|
this.gameRecord.push(move)
|
||||||
|
this.turn*= -1;
|
||||||
|
return { board: this.getBoardState(), meta: this.getMeta()};
|
||||||
|
}
|
||||||
|
|
||||||
|
clickBoard = (evt) => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
if (gameState.pass > 1 || gameState.winner) return editTerritory(evt);
|
||||||
|
// checks for placement and pushes to cell
|
||||||
|
let placement = [ parseInt(evt.target.closest('td').id.split('-')[0]), parseInt(evt.target.closest('td').id.split('-')[1]) ];
|
||||||
|
let point = findPointFromIdx(placement);
|
||||||
|
//checks that this placement was marked as legal
|
||||||
|
if ( !checkLegal(point) ) return;
|
||||||
|
clearKo();
|
||||||
|
clearPass();
|
||||||
|
resolveCaptures(point);
|
||||||
|
point.stone = gameState.turn;
|
||||||
|
point.joinGroup();
|
||||||
|
playSound(point);
|
||||||
|
clearCaptures();
|
||||||
|
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: ${point.pos}`)
|
||||||
|
gameState.turn*= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Point {
|
||||||
|
constructor(x, y, Game) {
|
||||||
|
this.pos = [ x, y ]
|
||||||
|
this.stone = 0; // this is where move placement will go 0, 1, -1, also contains ko: 'k'
|
||||||
|
this.legal;
|
||||||
|
this.territory;
|
||||||
|
this.capturing = [];
|
||||||
|
this.groupMembers = [ this ];
|
||||||
|
this.neighbors = {
|
||||||
|
top: {},
|
||||||
|
btm: {},
|
||||||
|
lft: {},
|
||||||
|
rgt: {}
|
||||||
|
}
|
||||||
|
this.neighbors.top = x > 1 ? [ x - 1, y ] : null;
|
||||||
|
this.neighbors.btm = x < Game.boardSize ? [ x + 1, y ] : null;
|
||||||
|
this.neighbors.rgt = y < Game.boardSize ? [ x, y + 1 ] : null;
|
||||||
|
this.neighbors.lft = y > 1 ? [ x, y - 1 ] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNeighbors = (Game) => {
|
||||||
|
let neighborsArr = [];
|
||||||
|
for (let neighbor in this.neighbors) {
|
||||||
|
let nbr = this.neighbors[neighbor];
|
||||||
|
// neighbor exists it's point is stored as { rPos, cPos}
|
||||||
|
if ( nbr !== null ) {
|
||||||
|
neighborsArr.push(Game.boardState.find(pt => pt.pos[0] === nbr[0] && pt.pos[1] === nbr[1]))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// returns array of existing neighbors to calling function
|
||||||
|
return neighborsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLiberties = (Game) => {
|
||||||
|
let neighborsArr = this.checkNeighbors(Game).filter(pt => pt.stone === 0);
|
||||||
|
return neighborsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
joinGroup = (Game) => {
|
||||||
|
this.groupMembers = this.groupMembers.filter(grp => grp.stone === this.stone);
|
||||||
|
this.groupMembers.push(this);
|
||||||
|
let frns = this.checkNeighbors(Game).filter(nbr => nbr.stone === this.stone);
|
||||||
|
for (let frn of frns) {
|
||||||
|
this.groupMembers.push(frn);
|
||||||
|
}
|
||||||
|
this.groupMembers = Array.from(new Set(this.groupMembers));
|
||||||
|
for (let grpMem in this.groupMembers) {
|
||||||
|
this.groupMembers = Array.from(new Set(this.groupMembers.concat(this.groupMembers[grpMem].groupMembers)));
|
||||||
|
}
|
||||||
|
for (let grpMem in this.groupMembers) {
|
||||||
|
this.groupMembers[grpMem].groupMembers = Array.from(new Set(this.groupMembers[grpMem].groupMembers.concat(this.groupMembers)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCapture = (Game) => {
|
||||||
|
let opps = this.checkNeighbors(Game).filter(nbr => nbr.stone === Game.turn * -1
|
||||||
|
&& nbr.getLiberties(Game).every(liberty => liberty === this));
|
||||||
|
for (let opp of opps) {
|
||||||
|
if (opp.groupMembers.every(stone => stone.getLiberties().filter(liberty => liberty !== this).length === 0)) {
|
||||||
|
this.capturing = this.capturing.concat(opp.groupMembers);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.capturing = Array.from(new Set(this.capturing));
|
||||||
|
return this.capturing;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkGroup = () => { // liberty is true when called by move false when called by check Capture
|
||||||
|
let frns = this.checkNeighbors().filter(nbr => nbr.stone === gameState.turn);
|
||||||
|
for (let frn in frns) {
|
||||||
|
if (frns[frn].groupMembers.find(stone => stone.getLiberties().find(liberty => liberty !== this))) return true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cycleTerritory = () => {
|
||||||
|
if (this.stone) {
|
||||||
|
this.groupMembers.forEach(pt => pt.territory = pt.territory * -1);
|
||||||
|
} else {
|
||||||
|
this.groupMembers.forEach(pt => {
|
||||||
|
switch (pt.territory) {
|
||||||
|
case 1:
|
||||||
|
pt.territory = -1;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
pt.territory = 'd';
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
pt.territory = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearKo(Game) {
|
||||||
|
for (let point in Game.boardState) {
|
||||||
|
point = Game.boardState[point];
|
||||||
|
point.stone = point.stone === 'k' ? 0 : point.stone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearPass(Game) {
|
||||||
|
Game.pass = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveCaptures(point, Game) {
|
||||||
|
if(!point.capturing.length) {
|
||||||
|
point.checkCapture(Game);
|
||||||
|
}
|
||||||
|
if(point.capturing.length) {
|
||||||
|
point.capturing.forEach(cap => {
|
||||||
|
Game.playerState[gameState.turn > 0 ? 'bCaptures' : 'wCaptures']++;
|
||||||
|
cap.stone = checkKo(point) ? 'k' : 0;
|
||||||
|
cap.groupMembers = [];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkLegal(point, Game) {
|
||||||
|
// clearOverlay();
|
||||||
|
// first step in logic: is point occupied, or in ko
|
||||||
|
if (point.stone) return 0;
|
||||||
|
// if point is not empty check if liberties
|
||||||
|
if (point.getLiberties(Game).length < 1) {
|
||||||
|
//if no liberties check if enemy group has liberties
|
||||||
|
if ( point.checkCapture(Game).length ) return 'l';
|
||||||
|
//if neighboring point is not empty check if friendly group is alive
|
||||||
|
if (point.checkGroup(Game)) return 'l';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 'l';
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearOverlay() {
|
||||||
|
for (let point in boardState) {
|
||||||
|
point = boardState[point];
|
||||||
|
point.legal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkKo(point) { // currently prevents snapback // capturing point has no liberties and is only capturing one stone and
|
||||||
|
if (!point.getLiberties().length && point.capturing.length === 1 && !point.checkNeighbors().some(stone => stone.stone === gameState.turn)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearCaptures(Game) {
|
||||||
|
for (let point in Game.boardState) {
|
||||||
|
point = Game.boardState[point];
|
||||||
|
point.capturing = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playerPass() {
|
||||||
|
// display confirmation message
|
||||||
|
clearKo();
|
||||||
|
clearCaptures();
|
||||||
|
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: pass`)
|
||||||
|
gameState.pass++;
|
||||||
|
if (gameState.pass === 2) return endGame();
|
||||||
|
gameState.turn*= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*----- endgame functions -----*/
|
||||||
|
|
||||||
|
function playerResign() {
|
||||||
|
// display confirmation message
|
||||||
|
gameState.pass = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickGameHud() {
|
||||||
|
if (gameState.pass > 1 && !gameState.winner) calculateWinner();
|
||||||
|
if (gameState.pass < 0) confirmResign();
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmResign() {
|
||||||
|
gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: resign`);
|
||||||
|
gameState.winner = STONES_DATA[gameState.turn * -1];
|
||||||
|
endGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function endGame() {
|
||||||
|
if (!gameState.winner) endGameSetTerritory()
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateWinner() {
|
||||||
|
let whiteTerritory = boardState.reduce((acc, pt) => {
|
||||||
|
if (pt.territory === -1 && pt.stone !== -1) {
|
||||||
|
return acc = acc + (pt.stone === 0 ? 1 : 2);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
let blackTerritory = boardState.reduce((acc, pt) => {
|
||||||
|
if (pt.territory === 1 && pt.stone !== 1) {
|
||||||
|
return acc + (pt.stone === 0 ? 1 : 2);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
gameState.playerState.wScore =
|
||||||
|
gameState.playerState.wCaptures
|
||||||
|
+ (gameState.komi < 0 ? gameState.komi * -1 : 0)
|
||||||
|
+ whiteTerritory;
|
||||||
|
gameState.playerState.bScore =
|
||||||
|
gameState.playerState.bCaptures
|
||||||
|
+ (gameState.komi > 0 ? gameState.komi : 0)
|
||||||
|
+ blackTerritory;
|
||||||
|
gameState.winner = gameState.playerState.wScore > gameState.playerState.bScore ? -1 : 1;
|
||||||
|
gameState.gameRecord.push(`${STONES_DATA[gameState.winner]}: +${Math.abs(gameState.playerState.wScore - gameState.playerState.bScore)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function endGameSetTerritory() {
|
||||||
|
let emptyPoints = boardState.filter(pt => !pt.stone);
|
||||||
|
emptyPoints.forEach(pt => pt.joinGroup());
|
||||||
|
emptyPointSetTerritory(emptyPoints);
|
||||||
|
groupsMarkDeadLive();
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupsMarkDeadLive() {
|
||||||
|
boardState.filter(pt => (!pt.territory ))
|
||||||
|
.forEach(pt => {
|
||||||
|
if (pt.groupMembers.some(grpMem => {
|
||||||
|
return grpMem.checkNeighbors().some(nbr => nbr.territory === pt.stone && nbr.stone === 0)
|
||||||
|
})) {
|
||||||
|
pt.groupMembers.forEach(grpMem => grpMem.territory = pt.stone);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
boardState.filter(pt => (!pt.territory)).forEach(pt => {
|
||||||
|
pt.territory = pt.stone * -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyPointSetTerritory(emptyPoints) {
|
||||||
|
emptyPoints.filter(pt => !pt.territory && pt.checkNeighbors().filter(nbr => nbr.stone !== 0))
|
||||||
|
.forEach(pt => {
|
||||||
|
let b = pt.groupMembers.reduce((acc, grpMem) => {
|
||||||
|
let bNbr = grpMem.checkNeighbors().filter(nbr => nbr.stone === 1).length;
|
||||||
|
return acc + bNbr;
|
||||||
|
}, 0);
|
||||||
|
let w = pt.groupMembers.reduce((acc, grpMem) => {
|
||||||
|
let wNbr = grpMem.checkNeighbors().filter(nbr => nbr.stone === -1).length;
|
||||||
|
return acc + wNbr;
|
||||||
|
}, 0);
|
||||||
|
pt.groupMembers.forEach(grp => {
|
||||||
|
if (Math.abs(b - w) < 4 && b && w) grp.territory = 'd'
|
||||||
|
else grp.territory = b > w ? 1 : -1;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Game
|
||||||
|
}
|
|
@ -1,33 +1,157 @@
|
||||||
const Game = require('./Game').Game;
|
const Game = require("./Game").Game;
|
||||||
|
|
||||||
const gamesInProgress = { }
|
|
||||||
|
|
||||||
|
const GameService = ({ moveQueries, gameQueries }) => {
|
||||||
const storeGame = (game) => {
|
const storeGame = (game) => {
|
||||||
gamesInProgress[game.id] = new Game(game);
|
gamesInProgress[game.id] = Game(game);
|
||||||
}
|
return gamesInProgress[game.id];
|
||||||
|
};
|
||||||
|
const gamesInProgress = {};
|
||||||
|
|
||||||
const initGame = (game) => {
|
const storeMove = (gameId) => async ({ player, pos: { x, y } }) => {
|
||||||
gamesInProgress[game.id] = new Game(game)
|
let move = { player, pos: { x, y } };
|
||||||
return gamesInProgress[game.id].initGame();
|
try {
|
||||||
|
if (moveQueries) {
|
||||||
|
const { id } = await moveQueries.addMove({
|
||||||
|
gameId,
|
||||||
|
player,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
gameRecord: true,
|
||||||
|
priorMove: null,
|
||||||
|
});
|
||||||
|
move.id = id;
|
||||||
|
move.success = true;
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
const makeMove = (game, move) => {
|
console.log(e);
|
||||||
if (!gamesInProgress[game.id]) initGame(game);
|
move.success = false;
|
||||||
const newState = gamesInProgress[game.id].makeMove(move);
|
} finally {
|
||||||
return {...newState}
|
return move;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getBoard = (gameId) => {
|
return {
|
||||||
return gamesInProgress[gameId].getBoardState();
|
initGame({ id, gameRecord = [], ...gameData }) {
|
||||||
|
if (gamesInProgress[id]) return this.getDataForUI(id);
|
||||||
|
if (gameRecord.length) {
|
||||||
|
gamesInProgress[id] = Game({ gameData, gameRecord });
|
||||||
|
} else {
|
||||||
|
gamesInProgress[id] = Game({ gameData }).initGame();
|
||||||
}
|
}
|
||||||
|
return this.getDataForUI(id);
|
||||||
|
},
|
||||||
|
|
||||||
const getAllGames = () => {
|
async makeMove({ id, move }) {
|
||||||
|
// check cache
|
||||||
|
if (!gamesInProgress[id]) {
|
||||||
|
try {
|
||||||
|
let gameRecord;
|
||||||
|
if (moveQueries) {
|
||||||
|
gameRecord = await moveQueries.findGameRecord(id);
|
||||||
|
}
|
||||||
|
storeGame({ id, gameRecord }).initGame();
|
||||||
|
} catch {
|
||||||
|
return { message: "error restoring game" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gamesInProgress[id] = await gamesInProgress[id].checkMove(move);
|
||||||
|
gamesInProgress[id] = gamesInProgress[id].makeMove(move);
|
||||||
|
if (gamesInProgress[id].success === false)
|
||||||
|
return { message: "illegal move" };
|
||||||
|
try {
|
||||||
|
if (moveQueries) {
|
||||||
|
const priorMove = gamesInProgress[id].gameRecord.length;
|
||||||
|
const moveInsert = {
|
||||||
|
gameId: id,
|
||||||
|
player: move.player,
|
||||||
|
x: move.pos.x,
|
||||||
|
y: move.pos.y,
|
||||||
|
gameRecord: true,
|
||||||
|
priorMove,
|
||||||
|
};
|
||||||
|
let moveDbResult;
|
||||||
|
moveDbResult = await moveQueries.addMove(moveInsert);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
gamesInProgress[id].returnToMove(-1);
|
||||||
|
} finally {
|
||||||
|
return this.getDataForUI(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getDataForUI: (id) => {
|
||||||
|
return {
|
||||||
|
board: gamesInProgress[id].legalMoves,
|
||||||
|
territory: gamesInProgress[id].territory,
|
||||||
|
...gamesInProgress[id].getMeta(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
dropGame: (id) => {
|
||||||
|
return { message: `${delete gamesInProgress[id]}` };
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllGames: () => {
|
||||||
return gamesInProgress;
|
return gamesInProgress;
|
||||||
}
|
},
|
||||||
|
|
||||||
module.exports = {
|
resign: ({ id, player }) => {
|
||||||
makeMove,
|
// add resign gamesQueries
|
||||||
getAllGames,
|
return gamesInProgress[id].submitResign(player).getMeta();
|
||||||
getBoard,
|
},
|
||||||
initGame
|
|
||||||
|
async pass({ id, player }) {
|
||||||
|
gamesInProgress[id] = gamesInProgress[id].submitPass(player);
|
||||||
|
if (gamesInProgress[id].success === false)
|
||||||
|
return { message: "illegal move" };
|
||||||
|
try {
|
||||||
|
if (moveQueries) {
|
||||||
|
const priorMove = gamesInProgress[id].gameRecord.length;
|
||||||
|
const movePass = {
|
||||||
|
gameId: id,
|
||||||
|
player,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
gameRecord: true,
|
||||||
|
priorMove,
|
||||||
|
};
|
||||||
|
let moveDbResult;
|
||||||
|
moveDbResult = await moveQueries.addMove(movePass);
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
gamesInProgress[id].returnToMove(-1);
|
||||||
|
} finally {
|
||||||
|
return this.getDataForUI(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleTerritory({ id, point }) {
|
||||||
|
gamesInProgress[id] = gamesInProgress[id].toggleTerritory(point);
|
||||||
|
return this.getDataForUI(id);
|
||||||
|
},
|
||||||
|
|
||||||
|
async endGame({ id }) {
|
||||||
|
gamesInProgress[id] = gamesInProgress[id].endGame();
|
||||||
|
const { winner, score, playerState } = gamesInProgress[id];
|
||||||
|
const { bCaptures, wCaptures } = playerState;
|
||||||
|
const winType = winner > 0 ? "B+" : "W+";
|
||||||
|
try {
|
||||||
|
if (gameQueries) {
|
||||||
|
const result = await gameQueries.endGame({
|
||||||
|
id,
|
||||||
|
winType,
|
||||||
|
score,
|
||||||
|
bCaptures,
|
||||||
|
wCaptures,
|
||||||
|
});
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
return this.getDataForUI(id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = GameService;
|
||||||
|
|
25
packages/server/services/guestServices.js
Normal file
25
packages/server/services/guestServices.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
const generateRandomPassword = () => {
|
||||||
|
const minLength = 8,
|
||||||
|
maxLength = 16,
|
||||||
|
minUTF = 33,
|
||||||
|
maxUTF = 126;
|
||||||
|
const randomize = (min, max) => Math.floor(Math.random() * (max - min) + min);
|
||||||
|
return Array(randomize(minLength, maxLength))
|
||||||
|
.fill(0)
|
||||||
|
.map(() => String.fromCharCode(randomize(minUTF, maxUTF)))
|
||||||
|
.join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const guestService = {
|
||||||
|
currentGuest: 0,
|
||||||
|
generateGuest() {
|
||||||
|
// generate unique username
|
||||||
|
const username = `Guest-${String(this.currentGuest++).padStart(6, 0)}`;
|
||||||
|
// generate random "password"
|
||||||
|
// this exists solely to add extra randomness to signed token and is not validated
|
||||||
|
const password = generateRandomPassword();
|
||||||
|
return { username, password };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = guestService;
|
|
@ -1,53 +1,157 @@
|
||||||
// TODO const someSocketLogic = require('./middleware/socketssockets/...');
|
// TODO const someSocketLogic = require('./middleware/sockets/...');
|
||||||
const socketIO = require('socket.io');
|
const socketIO = require("socket.io");
|
||||||
const io = socketIO({ cookie: false });
|
const io = socketIO({ cookie: false });
|
||||||
|
|
||||||
const gameQueries = require('./data/queries/game');
|
// const gameQueries = require('./data/queries/game');
|
||||||
const gameServices = require('./services/gameServices');
|
const moveQueries = require("./data/queries/move");
|
||||||
|
const gameQueries = require("./data/queries/game");
|
||||||
|
const gameServices = require("./services/gameServices")({
|
||||||
|
moveQueries,
|
||||||
|
gameQueries,
|
||||||
|
});
|
||||||
|
|
||||||
io.on('connection', socket=> {
|
io.on("connection", async (socket) => {
|
||||||
socket.emit('connected', {message: 'socket connected'});
|
socket.emit("connected", { message: "socket connected" });
|
||||||
socket.on('connect_room', data => {
|
socket.on("connect_room", async (data) => {
|
||||||
if (data.user && data.user.email) {
|
if (data.user && data.user.email) {
|
||||||
delete data.user.email;
|
delete data.user.email;
|
||||||
}
|
}
|
||||||
const room = data.room;
|
const room = data.room;
|
||||||
const roomIo = io.of(room);
|
const roomIo = io.of(room);
|
||||||
roomIo.on('connection', socket => {
|
roomIo.on("connection", async (socket) => {
|
||||||
socket.emit('connected')
|
socket.emit("connected");
|
||||||
socket.emit('new_user', data);
|
socket.emit("new_user", data);
|
||||||
socket.on('connect_game', data => {
|
socket.on("connect_game", (data) => {
|
||||||
const game = `game-${data.game.id}`;
|
const game = `game-${data.game.id}`;
|
||||||
socket.join(game, async () => {
|
socket.join(game, async () => {
|
||||||
// ! temp
|
// TODO move this logic into game service
|
||||||
gameServices.initGame({id: data.game.id})
|
const gameData = await gameQueries.findGameById(data.game.id);
|
||||||
// ! end-temp
|
const convertWinType = (winType) => {
|
||||||
const gameData = await gameServices.getBoard(data.game.id);
|
if (winType.includes("B")) return 1;
|
||||||
io.of(room).to(game).emit('game_connected', gameData)
|
if (winType.includes("W")) return -1;
|
||||||
|
if (winType.includes("0")) return "D";
|
||||||
|
return "?";
|
||||||
|
};
|
||||||
|
gameData.winner = gameData.win_type
|
||||||
|
? convertWinType(gameData.win_type)
|
||||||
|
: 0;
|
||||||
|
const gameRecord = await moveQueries.findGameRecord(data.game.id);
|
||||||
|
await gameServices.initGame({
|
||||||
|
id: data.game.id,
|
||||||
|
gameRecord,
|
||||||
|
gameData,
|
||||||
|
});
|
||||||
|
const { board, ...meta } = await gameServices.getDataForUI(
|
||||||
|
data.game.id
|
||||||
|
);
|
||||||
|
io.of(room).to(game).emit("game_connected", { board, meta });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
socket.on('make_move', data => {
|
|
||||||
|
|
||||||
|
// MAKE MOVE
|
||||||
|
socket.on("make_move", async (data) => {
|
||||||
const { user, move, board, game, room } = data;
|
const { user, move, board, game, room } = data;
|
||||||
const gameNsp = `game-${data.game.id}`;
|
const gameNsp = `game-${data.game.id}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updatedBoard = gameServices.makeMove(1, move);
|
const { board, message, ...meta } = await gameServices.makeMove({
|
||||||
|
id: data.game.id,
|
||||||
|
move,
|
||||||
|
});
|
||||||
|
const socketAction = message ? "error" : "update_board";
|
||||||
socket.join(gameNsp, () => {
|
socket.join(gameNsp, () => {
|
||||||
io.of(room).to(gameNsp).emit('update_board', updatedBoard)
|
io.of(room)
|
||||||
|
.to(gameNsp)
|
||||||
|
.emit(socketAction, { board, meta, message });
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
socket.join(gameNsp, () => {
|
||||||
|
io.of(room).to(gameNsp).emit("error", e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (err) {
|
|
||||||
socket.join(gameNsp, () => {
|
|
||||||
io.of(room).to(gameNsp).emit('error', err)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
// RESIGN
|
||||||
})
|
socket.on("resign", async ({ game, player }) => {
|
||||||
|
const { id, room } = game;
|
||||||
|
const gameNsp = `game-${id}`;
|
||||||
|
try {
|
||||||
|
const meta = await gameServices.resign({
|
||||||
|
id,
|
||||||
|
player,
|
||||||
|
});
|
||||||
|
socket.join(gameNsp, () => {
|
||||||
|
io.of(room).to(gameNsp).emit("game_resign", meta);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// PASS
|
||||||
|
socket.on("pass", async ({ game, player }) => {
|
||||||
|
const { id, room } = game;
|
||||||
|
const gameNsp = `game${id}`;
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
board,
|
||||||
|
message,
|
||||||
|
territory,
|
||||||
|
...meta
|
||||||
|
} = await gameServices.pass({
|
||||||
|
id,
|
||||||
|
player,
|
||||||
|
});
|
||||||
|
socket.join(gameNsp, () => {
|
||||||
|
io.of(room)
|
||||||
|
.to(gameNsp)
|
||||||
|
.emit("update_board", { board, message, territory, meta });
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TOGGLE TERRITORY
|
||||||
|
socket.on("toggle_territory", async ({ user, point, board, game }) => {
|
||||||
|
const { id, room } = game;
|
||||||
|
const gameNsp = `game${id}`;
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
board,
|
||||||
|
territory,
|
||||||
|
...meta
|
||||||
|
} = await gameServices.toggleTerritory({
|
||||||
|
id,
|
||||||
|
point,
|
||||||
|
});
|
||||||
|
socket.join(gameNsp, () => {
|
||||||
|
io.of(room)
|
||||||
|
.to(gameNsp)
|
||||||
|
.emit("update_board", { board, territory, meta });
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// END GAME
|
||||||
|
socket.on("end_game", async ({ user, game }) => {
|
||||||
|
const { id, room } = game;
|
||||||
|
const gameNsp = `game${id}`;
|
||||||
|
try {
|
||||||
|
const { board, ...meta } = await gameServices.endGame({ id });
|
||||||
|
socket.join(gameNsp, () => {
|
||||||
|
io.of(room).to(gameNsp).emit("end_game", { board, meta });
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
io
|
io,
|
||||||
}
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,75 +1,434 @@
|
||||||
const chai = require('chai');
|
const chai = require("chai");
|
||||||
const should = chai.should();
|
const should = chai.should();
|
||||||
const gameServices = require('../services/gameServices');
|
const gameServices = require("../services/gameServices")({});
|
||||||
|
|
||||||
describe('game services', () => {
|
describe("game services", () => {
|
||||||
it('init game returns game board', done => {
|
afterEach(() => gameServices.dropGame(1));
|
||||||
gameServices.initGame({id: 1, handicap: 4})
|
|
||||||
gameServices.getBoard(1).should.eql(fourHandicapBoard)
|
it("init game returns game board", (done) => {
|
||||||
|
gameServices
|
||||||
|
.initGame({ id: 1, handicap: 4 })
|
||||||
|
.board.should.eql(fourHandicapBoard);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('games services places move', done => {
|
it("init game returns game metadata", (done) => {
|
||||||
gameServices.initGame({id: 1, handicap: 4})
|
const { board, ...game } = gameServices.initGame({ id: 1, handicap: 4 });
|
||||||
const afterMoveOne = gameServices.makeMove({id: 1}, {player: 'white', pos: { x:6, y:3 }});
|
game.should.eql({ ...initialMeta, handicap: 4, turn: -1, territory: {} });
|
||||||
const afterMoveOneShould = { board:{ ...fourHandicapBoard, '6-3': -1}, meta: moveOneMeta };
|
|
||||||
afterMoveOne.should.eql(afterMoveOneShould);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('illegal move throws error', done => {
|
it("games services places move", async () => {
|
||||||
try {
|
|
||||||
gameServices.initGame({id: 1, handicap: 4})
|
|
||||||
const afterIllegalMove = gameServices.makeMove({id: 1}, {player: 'white', pos: { x:4, y:4 }});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
err.message.should.equal('illegal move')
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('game services places move next to stone', done => {
|
|
||||||
gameServices.initGame({ id: 1, handicap: 4 });
|
gameServices.initGame({ id: 1, handicap: 4 });
|
||||||
const afterMoveOne = gameServices.makeMove({ id: 1 }, { player: 'white', pos: { x: 4, y: 3 } });
|
const move = { player: "white", pos: { x: 6, y: 3 } };
|
||||||
afterMoveOne.should.not.eql(fourHandicapBoard);
|
const afterMove = await gameServices.makeMove({ id: 1, move });
|
||||||
done();
|
const afterMoveShould = {
|
||||||
})
|
board: { ...fourHandicapBoard, "6-3": -1 },
|
||||||
})
|
...initialMeta,
|
||||||
|
handicap: 4,
|
||||||
|
turn: 1,
|
||||||
|
gameRecord: [move],
|
||||||
|
territory: {},
|
||||||
|
};
|
||||||
|
afterMove.should.eql(afterMoveShould);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("illegal move returns error message", async () => {
|
||||||
|
gameServices.initGame({ id: 1, handicap: 4 });
|
||||||
|
const afterMove = await gameServices.makeMove({
|
||||||
|
id: 1,
|
||||||
|
move: { player: "white", pos: { x: 4, y: 4 } },
|
||||||
|
});
|
||||||
|
afterMove.message.should.equal("illegal move");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("game services places move next to stone", async () => {
|
||||||
|
gameServices.initGame({ id: 1, handicap: 4 });
|
||||||
|
const afterMove = await gameServices.makeMove({
|
||||||
|
id: 1,
|
||||||
|
move: { player: "white", pos: { x: 4, y: 3 } },
|
||||||
|
});
|
||||||
|
afterMove.board.should.eql({ ...fourHandicapBoard, "4-3": -1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const fourHandicapBoard = {
|
const fourHandicapBoard = {
|
||||||
'1-1': 'l','1-2': 'l','1-3': 'l','1-4': 'l','1-5': 'l','1-6': 'l','1-7': 'l','1-8': 'l','1-9': 'l','1-10': 'l','1-11': 'l','1-12': 'l','1-13': 'l','1-14': 'l','1-15': 'l','1-16': 'l','1-17': 'l','1-18': 'l','1-19': 'l',
|
"1-1": "l",
|
||||||
'2-1': 'l','2-2': 'l','2-3': 'l','2-4': 'l','2-5': 'l','2-6': 'l','2-7': 'l','2-8': 'l','2-9': 'l','2-10': 'l','2-11': 'l','2-12': 'l','2-13': 'l','2-14': 'l','2-15': 'l','2-16': 'l','2-17': 'l','2-18': 'l','2-19': 'l',
|
"1-2": "l",
|
||||||
'3-1': 'l','3-2': 'l','3-3': 'l','3-4': 'l','3-5': 'l','3-6': 'l','3-7': 'l','3-8': 'l','3-9': 'l','3-10': 'l','3-11': 'l','3-12': 'l','3-13': 'l','3-14': 'l','3-15': 'l','3-16': 'l','3-17': 'l','3-18': 'l','3-19': 'l',
|
"1-3": "l",
|
||||||
'4-1': 'l','4-2': 'l','4-3': 'l','4-4': 1,'4-5': 'l','4-6': 'l','4-7': 'l','4-8': 'l','4-9': 'l','4-10': 'l','4-11': 'l','4-12': 'l','4-13': 'l','4-14': 'l','4-15': 'l','4-16': 1,'4-17': 'l','4-18': 'l','4-19': 'l',
|
"1-4": "l",
|
||||||
'5-1': 'l','5-2': 'l','5-3': 'l','5-4': 'l','5-5': 'l','5-6': 'l','5-7': 'l','5-8': 'l','5-9': 'l','5-10': 'l','5-11': 'l','5-12': 'l','5-13': 'l','5-14': 'l','5-15': 'l','5-16': 'l','5-17': 'l','5-18': 'l','5-19': 'l',
|
"1-5": "l",
|
||||||
'6-1': 'l','6-2': 'l','6-3': 'l','6-4': 'l','6-5': 'l','6-6': 'l','6-7': 'l','6-8': 'l','6-9': 'l','6-10': 'l','6-11': 'l','6-12': 'l','6-13': 'l','6-14': 'l','6-15': 'l','6-16': 'l','6-17': 'l','6-18': 'l','6-19': 'l',
|
"1-6": "l",
|
||||||
'7-1': 'l','7-2': 'l','7-3': 'l','7-4': 'l','7-5': 'l','7-6': 'l','7-7': 'l','7-8': 'l','7-9': 'l','7-10': 'l','7-11': 'l','7-12': 'l','7-13': 'l','7-14': 'l','7-15': 'l','7-16': 'l','7-17': 'l','7-18': 'l','7-19': 'l',
|
"1-7": "l",
|
||||||
'8-1': 'l','8-2': 'l','8-3': 'l','8-4': 'l','8-5': 'l','8-6': 'l','8-7': 'l','8-8': 'l','8-9': 'l','8-10': 'l','8-11': 'l','8-12': 'l','8-13': 'l','8-14': 'l','8-15': 'l','8-16': 'l','8-17': 'l','8-18': 'l','8-19': 'l',
|
"1-8": "l",
|
||||||
'9-1': 'l','9-2': 'l','9-3': 'l','9-4': 'l','9-5': 'l','9-6': 'l','9-7': 'l','9-8': 'l','9-9': 'l','9-10': 'l','9-11': 'l','9-12': 'l','9-13': 'l','9-14': 'l','9-15': 'l','9-16': 'l','9-17': 'l','9-18': 'l','9-19': 'l',
|
"1-9": "l",
|
||||||
'10-1': 'l','10-2': 'l','10-3': 'l','10-4': 'l','10-5': 'l','10-6': 'l','10-7': 'l','10-8': 'l','10-9': 'l','10-10': 'l','10-11': 'l','10-12': 'l','10-13': 'l','10-14': 'l','10-15': 'l','10-16': 'l','10-17': 'l','10-18': 'l','10-19': 'l',
|
"1-10": "l",
|
||||||
'11-1': 'l','11-2': 'l','11-3': 'l','11-4': 'l','11-5': 'l','11-6': 'l','11-7': 'l','11-8': 'l','11-9': 'l','11-10': 'l','11-11': 'l','11-12': 'l','11-13': 'l','11-14': 'l','11-15': 'l','11-16': 'l','11-17': 'l','11-18': 'l','11-19': 'l',
|
"1-11": "l",
|
||||||
'12-1': 'l','12-2': 'l','12-3': 'l','12-4': 'l','12-5': 'l','12-6': 'l','12-7': 'l','12-8': 'l','12-9': 'l','12-10': 'l','12-11': 'l','12-12': 'l','12-13': 'l','12-14': 'l','12-15': 'l','12-16': 'l','12-17': 'l','12-18': 'l','12-19': 'l',
|
"1-12": "l",
|
||||||
'13-1': 'l','13-2': 'l','13-3': 'l','13-4': 'l','13-5': 'l','13-6': 'l','13-7': 'l','13-8': 'l','13-9': 'l','13-10': 'l','13-11': 'l','13-12': 'l','13-13': 'l','13-14': 'l','13-15': 'l','13-16': 'l','13-17': 'l','13-18': 'l','13-19': 'l',
|
"1-13": "l",
|
||||||
'14-1': 'l','14-2': 'l','14-3': 'l','14-4': 'l','14-5': 'l','14-6': 'l','14-7': 'l','14-8': 'l','14-9': 'l','14-10': 'l','14-11': 'l','14-12': 'l','14-13': 'l','14-14': 'l','14-15': 'l','14-16': 'l','14-17': 'l','14-18': 'l','14-19': 'l',
|
"1-14": "l",
|
||||||
'15-1': 'l','15-2': 'l','15-3': 'l','15-4': 'l','15-5': 'l','15-6': 'l','15-7': 'l','15-8': 'l','15-9': 'l','15-10': 'l','15-11': 'l','15-12': 'l','15-13': 'l','15-14': 'l','15-15': 'l','15-16': 'l','15-17': 'l','15-18': 'l','15-19': 'l',
|
"1-15": "l",
|
||||||
'16-1': 'l','16-2': 'l','16-3': 'l','16-4': 1,'16-5': 'l','16-6': 'l','16-7': 'l','16-8': 'l','16-9': 'l','16-10': 'l','16-11': 'l','16-12': 'l','16-13': 'l','16-14': 'l','16-15': 'l','16-16': 1,'16-17': 'l','16-18': 'l','16-19': 'l',
|
"1-16": "l",
|
||||||
'17-1': 'l','17-2': 'l','17-3': 'l','17-4': 'l','17-5': 'l','17-6': 'l','17-7': 'l','17-8': 'l','17-9': 'l','17-10': 'l','17-11': 'l','17-12': 'l','17-13': 'l','17-14': 'l','17-15': 'l','17-16': 'l','17-17': 'l','17-18': 'l','17-19': 'l',
|
"1-17": "l",
|
||||||
'18-1': 'l','18-2': 'l','18-3': 'l','18-4': 'l','18-5': 'l','18-6': 'l','18-7': 'l','18-8': 'l','18-9': 'l','18-10': 'l','18-11': 'l','18-12': 'l','18-13': 'l','18-14': 'l','18-15': 'l','18-16': 'l','18-17': 'l','18-18': 'l','18-19': 'l',
|
"1-18": "l",
|
||||||
'19-1': 'l','19-2': 'l','19-3': 'l','19-4': 'l','19-5': 'l','19-6': 'l','19-7': 'l','19-8': 'l','19-9': 'l','19-10': 'l','19-11': 'l','19-12': 'l','19-13': 'l','19-14': 'l','19-15': 'l','19-16': 'l','19-17': 'l','19-18': 'l','19-19': 'l'
|
"1-19": "l",
|
||||||
|
"2-1": "l",
|
||||||
|
"2-2": "l",
|
||||||
|
"2-3": "l",
|
||||||
|
"2-4": "l",
|
||||||
|
"2-5": "l",
|
||||||
|
"2-6": "l",
|
||||||
|
"2-7": "l",
|
||||||
|
"2-8": "l",
|
||||||
|
"2-9": "l",
|
||||||
|
"2-10": "l",
|
||||||
|
"2-11": "l",
|
||||||
|
"2-12": "l",
|
||||||
|
"2-13": "l",
|
||||||
|
"2-14": "l",
|
||||||
|
"2-15": "l",
|
||||||
|
"2-16": "l",
|
||||||
|
"2-17": "l",
|
||||||
|
"2-18": "l",
|
||||||
|
"2-19": "l",
|
||||||
|
"3-1": "l",
|
||||||
|
"3-2": "l",
|
||||||
|
"3-3": "l",
|
||||||
|
"3-4": "l",
|
||||||
|
"3-5": "l",
|
||||||
|
"3-6": "l",
|
||||||
|
"3-7": "l",
|
||||||
|
"3-8": "l",
|
||||||
|
"3-9": "l",
|
||||||
|
"3-10": "l",
|
||||||
|
"3-11": "l",
|
||||||
|
"3-12": "l",
|
||||||
|
"3-13": "l",
|
||||||
|
"3-14": "l",
|
||||||
|
"3-15": "l",
|
||||||
|
"3-16": "l",
|
||||||
|
"3-17": "l",
|
||||||
|
"3-18": "l",
|
||||||
|
"3-19": "l",
|
||||||
|
"4-1": "l",
|
||||||
|
"4-2": "l",
|
||||||
|
"4-3": "l",
|
||||||
|
"4-4": 1,
|
||||||
|
"4-5": "l",
|
||||||
|
"4-6": "l",
|
||||||
|
"4-7": "l",
|
||||||
|
"4-8": "l",
|
||||||
|
"4-9": "l",
|
||||||
|
"4-10": "l",
|
||||||
|
"4-11": "l",
|
||||||
|
"4-12": "l",
|
||||||
|
"4-13": "l",
|
||||||
|
"4-14": "l",
|
||||||
|
"4-15": "l",
|
||||||
|
"4-16": 1,
|
||||||
|
"4-17": "l",
|
||||||
|
"4-18": "l",
|
||||||
|
"4-19": "l",
|
||||||
|
"5-1": "l",
|
||||||
|
"5-2": "l",
|
||||||
|
"5-3": "l",
|
||||||
|
"5-4": "l",
|
||||||
|
"5-5": "l",
|
||||||
|
"5-6": "l",
|
||||||
|
"5-7": "l",
|
||||||
|
"5-8": "l",
|
||||||
|
"5-9": "l",
|
||||||
|
"5-10": "l",
|
||||||
|
"5-11": "l",
|
||||||
|
"5-12": "l",
|
||||||
|
"5-13": "l",
|
||||||
|
"5-14": "l",
|
||||||
|
"5-15": "l",
|
||||||
|
"5-16": "l",
|
||||||
|
"5-17": "l",
|
||||||
|
"5-18": "l",
|
||||||
|
"5-19": "l",
|
||||||
|
"6-1": "l",
|
||||||
|
"6-2": "l",
|
||||||
|
"6-3": "l",
|
||||||
|
"6-4": "l",
|
||||||
|
"6-5": "l",
|
||||||
|
"6-6": "l",
|
||||||
|
"6-7": "l",
|
||||||
|
"6-8": "l",
|
||||||
|
"6-9": "l",
|
||||||
|
"6-10": "l",
|
||||||
|
"6-11": "l",
|
||||||
|
"6-12": "l",
|
||||||
|
"6-13": "l",
|
||||||
|
"6-14": "l",
|
||||||
|
"6-15": "l",
|
||||||
|
"6-16": "l",
|
||||||
|
"6-17": "l",
|
||||||
|
"6-18": "l",
|
||||||
|
"6-19": "l",
|
||||||
|
"7-1": "l",
|
||||||
|
"7-2": "l",
|
||||||
|
"7-3": "l",
|
||||||
|
"7-4": "l",
|
||||||
|
"7-5": "l",
|
||||||
|
"7-6": "l",
|
||||||
|
"7-7": "l",
|
||||||
|
"7-8": "l",
|
||||||
|
"7-9": "l",
|
||||||
|
"7-10": "l",
|
||||||
|
"7-11": "l",
|
||||||
|
"7-12": "l",
|
||||||
|
"7-13": "l",
|
||||||
|
"7-14": "l",
|
||||||
|
"7-15": "l",
|
||||||
|
"7-16": "l",
|
||||||
|
"7-17": "l",
|
||||||
|
"7-18": "l",
|
||||||
|
"7-19": "l",
|
||||||
|
"8-1": "l",
|
||||||
|
"8-2": "l",
|
||||||
|
"8-3": "l",
|
||||||
|
"8-4": "l",
|
||||||
|
"8-5": "l",
|
||||||
|
"8-6": "l",
|
||||||
|
"8-7": "l",
|
||||||
|
"8-8": "l",
|
||||||
|
"8-9": "l",
|
||||||
|
"8-10": "l",
|
||||||
|
"8-11": "l",
|
||||||
|
"8-12": "l",
|
||||||
|
"8-13": "l",
|
||||||
|
"8-14": "l",
|
||||||
|
"8-15": "l",
|
||||||
|
"8-16": "l",
|
||||||
|
"8-17": "l",
|
||||||
|
"8-18": "l",
|
||||||
|
"8-19": "l",
|
||||||
|
"9-1": "l",
|
||||||
|
"9-2": "l",
|
||||||
|
"9-3": "l",
|
||||||
|
"9-4": "l",
|
||||||
|
"9-5": "l",
|
||||||
|
"9-6": "l",
|
||||||
|
"9-7": "l",
|
||||||
|
"9-8": "l",
|
||||||
|
"9-9": "l",
|
||||||
|
"9-10": "l",
|
||||||
|
"9-11": "l",
|
||||||
|
"9-12": "l",
|
||||||
|
"9-13": "l",
|
||||||
|
"9-14": "l",
|
||||||
|
"9-15": "l",
|
||||||
|
"9-16": "l",
|
||||||
|
"9-17": "l",
|
||||||
|
"9-18": "l",
|
||||||
|
"9-19": "l",
|
||||||
|
"10-1": "l",
|
||||||
|
"10-2": "l",
|
||||||
|
"10-3": "l",
|
||||||
|
"10-4": "l",
|
||||||
|
"10-5": "l",
|
||||||
|
"10-6": "l",
|
||||||
|
"10-7": "l",
|
||||||
|
"10-8": "l",
|
||||||
|
"10-9": "l",
|
||||||
|
"10-10": "l",
|
||||||
|
"10-11": "l",
|
||||||
|
"10-12": "l",
|
||||||
|
"10-13": "l",
|
||||||
|
"10-14": "l",
|
||||||
|
"10-15": "l",
|
||||||
|
"10-16": "l",
|
||||||
|
"10-17": "l",
|
||||||
|
"10-18": "l",
|
||||||
|
"10-19": "l",
|
||||||
|
"11-1": "l",
|
||||||
|
"11-2": "l",
|
||||||
|
"11-3": "l",
|
||||||
|
"11-4": "l",
|
||||||
|
"11-5": "l",
|
||||||
|
"11-6": "l",
|
||||||
|
"11-7": "l",
|
||||||
|
"11-8": "l",
|
||||||
|
"11-9": "l",
|
||||||
|
"11-10": "l",
|
||||||
|
"11-11": "l",
|
||||||
|
"11-12": "l",
|
||||||
|
"11-13": "l",
|
||||||
|
"11-14": "l",
|
||||||
|
"11-15": "l",
|
||||||
|
"11-16": "l",
|
||||||
|
"11-17": "l",
|
||||||
|
"11-18": "l",
|
||||||
|
"11-19": "l",
|
||||||
|
"12-1": "l",
|
||||||
|
"12-2": "l",
|
||||||
|
"12-3": "l",
|
||||||
|
"12-4": "l",
|
||||||
|
"12-5": "l",
|
||||||
|
"12-6": "l",
|
||||||
|
"12-7": "l",
|
||||||
|
"12-8": "l",
|
||||||
|
"12-9": "l",
|
||||||
|
"12-10": "l",
|
||||||
|
"12-11": "l",
|
||||||
|
"12-12": "l",
|
||||||
|
"12-13": "l",
|
||||||
|
"12-14": "l",
|
||||||
|
"12-15": "l",
|
||||||
|
"12-16": "l",
|
||||||
|
"12-17": "l",
|
||||||
|
"12-18": "l",
|
||||||
|
"12-19": "l",
|
||||||
|
"13-1": "l",
|
||||||
|
"13-2": "l",
|
||||||
|
"13-3": "l",
|
||||||
|
"13-4": "l",
|
||||||
|
"13-5": "l",
|
||||||
|
"13-6": "l",
|
||||||
|
"13-7": "l",
|
||||||
|
"13-8": "l",
|
||||||
|
"13-9": "l",
|
||||||
|
"13-10": "l",
|
||||||
|
"13-11": "l",
|
||||||
|
"13-12": "l",
|
||||||
|
"13-13": "l",
|
||||||
|
"13-14": "l",
|
||||||
|
"13-15": "l",
|
||||||
|
"13-16": "l",
|
||||||
|
"13-17": "l",
|
||||||
|
"13-18": "l",
|
||||||
|
"13-19": "l",
|
||||||
|
"14-1": "l",
|
||||||
|
"14-2": "l",
|
||||||
|
"14-3": "l",
|
||||||
|
"14-4": "l",
|
||||||
|
"14-5": "l",
|
||||||
|
"14-6": "l",
|
||||||
|
"14-7": "l",
|
||||||
|
"14-8": "l",
|
||||||
|
"14-9": "l",
|
||||||
|
"14-10": "l",
|
||||||
|
"14-11": "l",
|
||||||
|
"14-12": "l",
|
||||||
|
"14-13": "l",
|
||||||
|
"14-14": "l",
|
||||||
|
"14-15": "l",
|
||||||
|
"14-16": "l",
|
||||||
|
"14-17": "l",
|
||||||
|
"14-18": "l",
|
||||||
|
"14-19": "l",
|
||||||
|
"15-1": "l",
|
||||||
|
"15-2": "l",
|
||||||
|
"15-3": "l",
|
||||||
|
"15-4": "l",
|
||||||
|
"15-5": "l",
|
||||||
|
"15-6": "l",
|
||||||
|
"15-7": "l",
|
||||||
|
"15-8": "l",
|
||||||
|
"15-9": "l",
|
||||||
|
"15-10": "l",
|
||||||
|
"15-11": "l",
|
||||||
|
"15-12": "l",
|
||||||
|
"15-13": "l",
|
||||||
|
"15-14": "l",
|
||||||
|
"15-15": "l",
|
||||||
|
"15-16": "l",
|
||||||
|
"15-17": "l",
|
||||||
|
"15-18": "l",
|
||||||
|
"15-19": "l",
|
||||||
|
"16-1": "l",
|
||||||
|
"16-2": "l",
|
||||||
|
"16-3": "l",
|
||||||
|
"16-4": 1,
|
||||||
|
"16-5": "l",
|
||||||
|
"16-6": "l",
|
||||||
|
"16-7": "l",
|
||||||
|
"16-8": "l",
|
||||||
|
"16-9": "l",
|
||||||
|
"16-10": "l",
|
||||||
|
"16-11": "l",
|
||||||
|
"16-12": "l",
|
||||||
|
"16-13": "l",
|
||||||
|
"16-14": "l",
|
||||||
|
"16-15": "l",
|
||||||
|
"16-16": 1,
|
||||||
|
"16-17": "l",
|
||||||
|
"16-18": "l",
|
||||||
|
"16-19": "l",
|
||||||
|
"17-1": "l",
|
||||||
|
"17-2": "l",
|
||||||
|
"17-3": "l",
|
||||||
|
"17-4": "l",
|
||||||
|
"17-5": "l",
|
||||||
|
"17-6": "l",
|
||||||
|
"17-7": "l",
|
||||||
|
"17-8": "l",
|
||||||
|
"17-9": "l",
|
||||||
|
"17-10": "l",
|
||||||
|
"17-11": "l",
|
||||||
|
"17-12": "l",
|
||||||
|
"17-13": "l",
|
||||||
|
"17-14": "l",
|
||||||
|
"17-15": "l",
|
||||||
|
"17-16": "l",
|
||||||
|
"17-17": "l",
|
||||||
|
"17-18": "l",
|
||||||
|
"17-19": "l",
|
||||||
|
"18-1": "l",
|
||||||
|
"18-2": "l",
|
||||||
|
"18-3": "l",
|
||||||
|
"18-4": "l",
|
||||||
|
"18-5": "l",
|
||||||
|
"18-6": "l",
|
||||||
|
"18-7": "l",
|
||||||
|
"18-8": "l",
|
||||||
|
"18-9": "l",
|
||||||
|
"18-10": "l",
|
||||||
|
"18-11": "l",
|
||||||
|
"18-12": "l",
|
||||||
|
"18-13": "l",
|
||||||
|
"18-14": "l",
|
||||||
|
"18-15": "l",
|
||||||
|
"18-16": "l",
|
||||||
|
"18-17": "l",
|
||||||
|
"18-18": "l",
|
||||||
|
"18-19": "l",
|
||||||
|
"19-1": "l",
|
||||||
|
"19-2": "l",
|
||||||
|
"19-3": "l",
|
||||||
|
"19-4": "l",
|
||||||
|
"19-5": "l",
|
||||||
|
"19-6": "l",
|
||||||
|
"19-7": "l",
|
||||||
|
"19-8": "l",
|
||||||
|
"19-9": "l",
|
||||||
|
"19-10": "l",
|
||||||
|
"19-11": "l",
|
||||||
|
"19-12": "l",
|
||||||
|
"19-13": "l",
|
||||||
|
"19-14": "l",
|
||||||
|
"19-15": "l",
|
||||||
|
"19-16": "l",
|
||||||
|
"19-17": "l",
|
||||||
|
"19-18": "l",
|
||||||
|
"19-19": "l",
|
||||||
};
|
};
|
||||||
|
|
||||||
const moveOneMeta = {
|
const initialMeta = {
|
||||||
gameRecord: [
|
winner: null,
|
||||||
{player: 'white', pos: { x:6, y:3 }}
|
turn: 0,
|
||||||
],
|
|
||||||
pass: 0,
|
pass: 0,
|
||||||
|
komi: 6.5,
|
||||||
|
handicap: 0,
|
||||||
|
boardSize: 19,
|
||||||
playerState: {
|
playerState: {
|
||||||
bCaptures: 0,
|
bCaptures: 0,
|
||||||
bScore: 0,
|
|
||||||
wCaptures: 0,
|
wCaptures: 0,
|
||||||
wScore: 0
|
bScore: 0,
|
||||||
|
wScore: 0,
|
||||||
},
|
},
|
||||||
turn: 1,
|
gameRecord: [],
|
||||||
winner: null
|
score: 0,
|
||||||
}
|
};
|
||||||
|
|
BIN
public/game-in-progress.png
Normal file
BIN
public/game-in-progress.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
BIN
public/game-logic.png
Normal file
BIN
public/game-logic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
BIN
public/game-module.png
Normal file
BIN
public/game-module.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
BIN
public/game-record.png
Normal file
BIN
public/game-record.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 809 KiB |
BIN
public/home-screen.png
Normal file
BIN
public/home-screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
BIN
public/room-screen.png
Normal file
BIN
public/room-screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 359 KiB |
Loading…
Reference in a new issue