Compare commits
32 commits
Author | SHA1 | Date | |
---|---|---|---|
a13b6f46a0 | |||
abd3e2edc1 | |||
a207d8e08b | |||
6c749a2216 | |||
4664d47aa9 | |||
1b0984f6d2 | |||
d6d9f30562 | |||
a39796fc55 | |||
e6325cf482 | |||
f7e60d17d1 | |||
de461a3da4 | |||
aad9977d79 | |||
547a46697a | |||
26203d9f54 | |||
ab5dd5c12a | |||
35726b7052 | |||
f4d4d16b87 | |||
63b522b6d2 | |||
e1378944ff | |||
e8e34b9c62 | |||
acb7a8d34a | |||
8e8dfc3cd6 | |||
46eb51e2b5 | |||
ae28d7be9c | |||
6f274a5e8a | |||
bf5f9d86b9 | |||
bc42a5bfa4 | |||
10a0dc348e | |||
9580139a78 | |||
6306da1abd | |||
203f24bd9c | |||
40115b714d |
16 changed files with 2309 additions and 5344 deletions
12
README.md
12
README.md
|
@ -1,11 +1,13 @@
|
||||||
# BSX
|
# McCarthyism
|
||||||
The card game BS, but better!
|
A subversive card game.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
`git clone` this repository, then run `npm install` in `front` and `back`. You may have to run it multiple times for it to pull in all dependencies.
|
Clone this repository, then run `npm install` in `front` and `back`.
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
You can use `NEXT_PUBLIC_BACK_HOST=localhost:4000 npm run dev` to run the frontend and `PORT=4000 node dist/index.js` to run the backend after building it with `npm run build`. You could also just run `NEXT_PUBLIC_BACK_HOST='https://server.exozy.me' npm run dev` if you are only developing the frontend and connect to the public backend server.
|
You can use `npm run build` to build the frontend and backend. Use `NEXT_PUBLIC_BACK_HOST=localhost:4000 npm run dev` to run the frontend and `PORT=4000 node dist/index.js` to run the backend.
|
||||||
|
|
||||||
|
You could also run `NEXT_PUBLIC_BACK_HOST='https://server.exozy.me' npm run dev` if you are only developing the frontend and connecting to the public backend server.
|
||||||
|
|
||||||
## Production deployment
|
## Production deployment
|
||||||
Edit the environmental variables in `./env` and use the script `./run`.
|
Edit and run the script `./run`.
|
||||||
|
|
1224
back/package-lock.json
generated
1224
back/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "bsx-back",
|
"name": "mccarthyism-back",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bsx-core": "file:../core",
|
"mccarthyism-core": "file:../core",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"socket.io": "^3.1.1"
|
"socket.io": "^3.1.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Card, Suit} from 'bsx-core';
|
import {Card, Suit} from 'mccarthyism-core';
|
||||||
|
|
||||||
import Client from './Client';
|
import Client from './Client';
|
||||||
import logSocket from './logSocket';
|
import logSocket from './logSocket';
|
||||||
|
@ -55,16 +55,32 @@ export default class Game {
|
||||||
for (let i = 1; i <= 13; ++i)
|
for (let i = 1; i <= 13; ++i)
|
||||||
for (let j = 0; j < 4; ++j)
|
for (let j = 0; j < 4; ++j)
|
||||||
cards.push({rank: i, suit: j});
|
cards.push({rank: i, suit: j});
|
||||||
// Shuffle cards
|
const handSize = 5 - Math.floor(this.room.clients.length/7);
|
||||||
for (let i = 0; i < 52; ++i) {
|
// Shuffle red cards
|
||||||
|
for (let i = 0; i < 26; ++i) {
|
||||||
const j = Math.floor(Math.random() * (i+1));
|
const j = Math.floor(Math.random() * (i+1));
|
||||||
[cards[i], cards[j]] = [cards[j], cards[i]];
|
[cards[i], cards[j]] = [cards[j], cards[i]];
|
||||||
}
|
}
|
||||||
const handSize = 5; // 5 - this.room.clients.length/7;
|
// Shuffle black cards
|
||||||
|
for (let i = 0; i < 26; ++i) {
|
||||||
|
const j = Math.floor(Math.random() * (i+1));
|
||||||
|
[cards[i+26], cards[j+26]] = [cards[j+26], cards[i+26]];
|
||||||
|
}
|
||||||
for (let i = 0; i < this.room.clients.length; ++i) {
|
for (let i = 0; i < this.room.clients.length; ++i) {
|
||||||
this.players.push(new Player(this, this.room.clients[i]));
|
this.players.push(new Player(this, this.room.clients[i]));
|
||||||
this.players[i].cards = cards.slice(i * handSize, (i + 1) * handSize);
|
this.players[i].cards.push(cards[i], cards[i+26]); // Make sure everyone has a red and black card
|
||||||
}
|
}
|
||||||
|
const remainingCards = [];
|
||||||
|
for (let i = this.room.clients.length; i < 26; ++i)
|
||||||
|
remainingCards.push(cards[i], cards[i+26]);
|
||||||
|
// Shuffle remaining cards
|
||||||
|
for (let i = 0; i < remainingCards.length; ++i) {
|
||||||
|
const j = Math.floor(Math.random() * (i+1));
|
||||||
|
[remainingCards[i], remainingCards[j]] = [remainingCards[j], remainingCards[i]];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < this.room.clients.length; ++i)
|
||||||
|
for (let j = 0; j < handSize-2; ++j)
|
||||||
|
this.players[i].cards.push(remainingCards[i*(handSize-2)+j]);
|
||||||
const startingPlayer = this.players[0]; // Pick a random starting player instead??
|
const startingPlayer = this.players[0]; // Pick a random starting player instead??
|
||||||
this.playerTurn = this.players.indexOf(startingPlayer);
|
this.playerTurn = this.players.indexOf(startingPlayer);
|
||||||
this.playersFinished = this.room.clients.length;
|
this.playersFinished = this.room.clients.length;
|
||||||
|
@ -92,8 +108,8 @@ export default class Game {
|
||||||
}
|
}
|
||||||
async round() {
|
async round() {
|
||||||
this.phase = 0;
|
this.phase = 0;
|
||||||
this.players.forEach((p: Player) => { p.stack = []; });
|
this.players.forEach((p: Player) => p.stack = []);
|
||||||
this.players.forEach((p: Player) => { p.flipped = []; });
|
this.players.forEach((p: Player) => p.flipped = []);
|
||||||
await this.prepare(); // Phase 0
|
await this.prepare(); // Phase 0
|
||||||
this.phase = 1;
|
this.phase = 1;
|
||||||
this.lastPlayed = 0;
|
this.lastPlayed = 0;
|
||||||
|
@ -179,8 +195,8 @@ export default class Game {
|
||||||
delete p.disconnectListener;
|
delete p.disconnectListener;
|
||||||
(() => {
|
(() => {
|
||||||
if (this.players[selectedPlayer].stack.length > 0) {
|
if (this.players[selectedPlayer].stack.length > 0) {
|
||||||
if (this.players[selectedPlayer].stack[0].suit === Suit.Diamonds ||
|
if (this.players[selectedPlayer].stack[0].suit === Suit.Spades ||
|
||||||
this.players[selectedPlayer].stack[0].suit === Suit.Hearts) this.phase = 3; // Red card
|
this.players[selectedPlayer].stack[0].suit === Suit.Clubs) this.phase = 3; // Red card
|
||||||
this.players[selectedPlayer].flipped.push(this.players[selectedPlayer].stack[0]);
|
this.players[selectedPlayer].flipped.push(this.players[selectedPlayer].stack[0]);
|
||||||
this.players[selectedPlayer].stack.splice(0, 1);
|
this.players[selectedPlayer].stack.splice(0, 1);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -3,15 +3,4 @@ import http from 'http';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import {Server} from 'socket.io';
|
import {Server} from 'socket.io';
|
||||||
|
|
||||||
// export default new Server({cors: {origin: process.env.ORIGIN, methods: ['GET', 'POST']}});
|
export default new Server(Number(process.env.PORT!), {cors: {origin: '*', methods: ['GET', 'POST']}});
|
||||||
|
|
||||||
const base = process.env.SSL_KEY && process.env.SSL_CERT && process.env.SSL_CA ? https.createServer({
|
|
||||||
"key": fs.readFileSync(process.env.SSL_KEY),
|
|
||||||
"cert": fs.readFileSync(process.env.SSL_CERT),
|
|
||||||
"ca": fs.readFileSync(process.env.SSL_CA)
|
|
||||||
}) : http.createServer();
|
|
||||||
base.listen(+process.env.PORT!, '0.0.0.0', () => {
|
|
||||||
console.log(`Listening on port ${process.env.PORT}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default new Server(base, {cors: {origin: process.env.ORIGIN, methods: ['GET', 'POST']}});
|
|
||||||
|
|
6
build
6
build
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
source ./env
|
|
||||||
cd front
|
|
||||||
npm run build
|
|
||||||
cd ../back
|
|
||||||
npm run build
|
|
16
core/package-lock.json
generated
16
core/package-lock.json
generated
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "bsx-core",
|
"name": "mccarthyism-core",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bsx-core",
|
"name": "mccarthyism-core",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -13,9 +13,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.1.5",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
|
||||||
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
|
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
@ -28,9 +28,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.1.5",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
|
||||||
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
|
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "bsx-core",
|
"name": "mccarthyism-core",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
enum Suit {
|
enum Suit {
|
||||||
Clubs,
|
|
||||||
Diamonds,
|
Diamonds,
|
||||||
Hearts,
|
Hearts,
|
||||||
|
Clubs,
|
||||||
Spades
|
Spades
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
env
6
env
|
@ -1,6 +0,0 @@
|
||||||
export NEXT_PUBLIC_BACK_HOST='https://server.exozy.me'
|
|
||||||
export ORIGIN="*"
|
|
||||||
export PORT=4000
|
|
||||||
export SSL_KEY=/etc/letsencrypt/live/exozy.me/privkey.pem
|
|
||||||
export SSL_CERT=/etc/letsencrypt/live/exozy.me/cert.pem
|
|
||||||
export SSL_CA=/etc/letsencrypt/live/exozy.me/chain.pem
|
|
5
front/next-env.d.ts
vendored
5
front/next-env.d.ts
vendored
|
@ -1,2 +1,5 @@
|
||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||||
|
|
6250
front/package-lock.json
generated
6250
front/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "bsx-front",
|
"name": "mccarthyism-front",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -10,8 +10,8 @@
|
||||||
"preinstall": "cd ../core && npm i && npm run build"
|
"preinstall": "cd ../core && npm i && npm run build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bsx-core": "file:../core",
|
"mccarthyism-core": "file:../core",
|
||||||
"next": "10.0.6",
|
"next": "^12.0.8",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
"socket.io-client": "^3.1.1"
|
"socket.io-client": "^3.1.1"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Card, Suit} from 'bsx-core';
|
import {Card, Suit} from 'mccarthyism-core';
|
||||||
import {useEffect, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
|
|
||||||
|
@ -16,19 +16,17 @@ interface GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
const rankStrs = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
|
const rankStrs = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
|
||||||
const suitChars = ['♣', '♦', '♥', '♠'];
|
const suitChars = ['♦', '♥', '♣', '♠'];
|
||||||
|
|
||||||
const rules = `Welcome to BSX!
|
const rules = `There are only 5 simple rules!
|
||||||
|
|
||||||
There are only 5 simple rules!
|
1. Each player will first be dealt the same number of cards.
|
||||||
|
|
||||||
1. You will first be dealt 5 cards.
|
|
||||||
|
|
||||||
2. At the beginning of each round, you must rearrange the order of your cards and place them face down in a stack.
|
2. At the beginning of each round, you must rearrange the order of your cards and place them face down in a stack.
|
||||||
|
|
||||||
3. During the round, players go around in a circle, claiming increasingly greater numbers. If it is you turn to claim a number, you must either claim a larger number than the previously claimed number or call BS. If you call BS, the previous player must flip over their claimed number of black (clubs or spades) cards from the tops of everyone's stacks.
|
3. During the round, players go around in a circle, claiming increasingly greater numbers. If it is your turn to claim a number, you must either claim a larger number than the previously claimed number or call BS. If you call BS, the previous player must flip over their claimed number of red (heart or diamond) cards from the tops of everyone's stacks.
|
||||||
|
|
||||||
4. If the previous player manages to flip over their claimed number of black cards, you must choose a card from your stack to give up. Otherwise, the previous player must choose one of their cards to give up.
|
4. When you click flip, it flips over the top card from that person's stack. If the previous player flips over a black card or cannot flip over their claimed number of red cards, they must choose a card from their stack to give up. Otherwise, the person who called BS must choose one of their cards to give up.
|
||||||
|
|
||||||
5. If you give up all your cards, you lose! Last player remaining wins!
|
5. If you give up all your cards, you lose! Last player remaining wins!
|
||||||
|
|
||||||
|
@ -88,6 +86,9 @@ export default function Game() {
|
||||||
if (!loggedIn) {
|
if (!loggedIn) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<h2>
|
||||||
|
Welcome to McCarthyism!
|
||||||
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
{rules}
|
{rules}
|
||||||
</div>
|
</div>
|
||||||
|
@ -99,6 +100,7 @@ export default function Game() {
|
||||||
}}
|
}}
|
||||||
username={username}
|
username={username}
|
||||||
/>
|
/>
|
||||||
|
This game is licensed under the AGPL. <a href="https://git.exozy.me/Ta180m/McCarthyism">Source code here!</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +160,7 @@ export default function Game() {
|
||||||
</div>
|
</div>
|
||||||
{`Rearrange your card stack from top to bottom!`}
|
{`Rearrange your card stack from top to bottom!`}
|
||||||
<div>
|
<div>
|
||||||
<p>Your cards:</p>
|
<p>Your cards stack:</p>
|
||||||
{gameState.cards.map((card, i) => (
|
{gameState.cards.map((card, i) => (
|
||||||
<label key={card.rank+' '+card.suit}>
|
<label key={card.rank+' '+card.suit}>
|
||||||
<div>
|
<div>
|
||||||
|
@ -262,7 +264,7 @@ export default function Game() {
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
{`${gameState.playerTurn} has called BS! ${gameState.lastPlayedPlayer} must flip over ${gameState.lastPlayed} black cards!`}
|
{`${gameState.playerTurn} has called BS! ${gameState.lastPlayedPlayer} must flip over ${gameState.lastPlayed} red cards!`}
|
||||||
<div>
|
<div>
|
||||||
<p>Stacks:</p>
|
<p>Stacks:</p>
|
||||||
{gameState.players.map((player, i) => (
|
{gameState.players.map((player, i) => (
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
@ -12,8 +16,15 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve"
|
"jsx": "preserve",
|
||||||
|
"incremental": true
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
"include": [
|
||||||
"exclude": ["node_modules"]
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
20
run
20
run
|
@ -1,5 +1,17 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/bash
|
||||||
source ./env
|
|
||||||
./build
|
# Environment variable
|
||||||
|
export NEXT_PUBLIC_BACK_HOST='https://7.exozy.me'
|
||||||
|
export FRONT_PORT=4206
|
||||||
|
export PORT=4207
|
||||||
|
|
||||||
|
# Build
|
||||||
cd front
|
cd front
|
||||||
npm run start -- -p 5000 | sudo -E node ../back/dist/index.js # sudo hack to read SSL files
|
npm run build
|
||||||
|
cd ../back
|
||||||
|
npm run build
|
||||||
|
echo 'Build complete'
|
||||||
|
|
||||||
|
# Run
|
||||||
|
cd ../front
|
||||||
|
npm run start -- -p $FRONT_PORT | node ../back/dist/index.js
|
||||||
|
|
Loading…
Reference in a new issue