Compare commits
47 commits
v0.1-alpha
...
main
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 | |||
45dd3cb2ef | |||
df75aa721e | |||
013018ecae | |||
02964186a8 | |||
e1d06740a9 | |||
cb24f26534 | |||
e5c942c941 | |||
8cf4626e99 | |||
78f5b677ea | |||
89507279cf | |||
c3a604c952 | |||
c68418252e | |||
d1c0d54c40 | |||
069e1d3a1f | |||
3834a5d710 |
15 changed files with 2448 additions and 5353 deletions
14
README.md
14
README.md
|
@ -1,5 +1,13 @@
|
|||
# BSX
|
||||
The card game BS, but better! Currently under development.
|
||||
# McCarthyism
|
||||
A subversive card game.
|
||||
|
||||
## Getting started
|
||||
Clone this repository, then run `npm install` in `front` and `back`.
|
||||
|
||||
## 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 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
|
||||
Edit and run the script `./run`.
|
||||
|
|
1230
back/package-lock.json
generated
1230
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",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
|
@ -13,7 +13,7 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bsx-core": "file:../core",
|
||||
"mccarthyism-core": "file:../core",
|
||||
"dotenv": "^8.2.0",
|
||||
"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 logSocket from './logSocket';
|
||||
|
@ -10,6 +10,7 @@ class Player {
|
|||
client: Client;
|
||||
cards: Card[] = [];
|
||||
stack: Card[] = [];
|
||||
flipped: Card[] = [];
|
||||
disconnected = false;
|
||||
disconnectListener?: () => void;
|
||||
rank = 0;
|
||||
|
@ -26,6 +27,7 @@ class Player {
|
|||
username: p.client.username,
|
||||
numCards: p.cards.length,
|
||||
stackSize: p.stack.length,
|
||||
flipped: p.flipped,
|
||||
rank: p.rank,
|
||||
})),
|
||||
phase: this.game.phase,
|
||||
|
@ -53,31 +55,45 @@ export default class Game {
|
|||
for (let i = 1; i <= 13; ++i)
|
||||
for (let j = 0; j < 4; ++j)
|
||||
cards.push({rank: i, suit: j});
|
||||
// Shuffle cards
|
||||
for (let i = 0; i < 52; ++i) {
|
||||
const handSize = 5 - Math.floor(this.room.clients.length/7);
|
||||
// Shuffle red cards
|
||||
for (let i = 0; i < 26; ++i) {
|
||||
const j = Math.floor(Math.random() * (i+1));
|
||||
[cards[i], cards[j]] = [cards[j], cards[i]];
|
||||
}
|
||||
const handSize = 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) {
|
||||
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??
|
||||
this.playerTurn = this.players.indexOf(startingPlayer);
|
||||
this.playersFinished = this.room.clients.length;
|
||||
// Run the game
|
||||
while (true) {
|
||||
// Check if game ended
|
||||
// Rewrite
|
||||
const playersLeft: Player[] = [];
|
||||
this.players.forEach((p: Player) => {
|
||||
if (!p.rank && !p.disconnected)
|
||||
playersLeft.push(p);
|
||||
if (!p.rank && !p.disconnected) {
|
||||
if (p.cards.length === 0) p.rank = this.playersFinished--;
|
||||
else playersLeft.push(p);
|
||||
}
|
||||
});
|
||||
if (playersLeft.length < 2) {
|
||||
if (playersLeft.length === 1)
|
||||
playersLeft[0].rank = ++this.playersFinished; // rank is reversed, fix later
|
||||
break;
|
||||
}
|
||||
if (playersLeft.length === 1) break;
|
||||
await this.round();
|
||||
}
|
||||
this.broadcastGameState();
|
||||
|
@ -92,6 +108,8 @@ export default class Game {
|
|||
}
|
||||
async round() {
|
||||
this.phase = 0;
|
||||
this.players.forEach((p: Player) => p.stack = []);
|
||||
this.players.forEach((p: Player) => p.flipped = []);
|
||||
await this.prepare(); // Phase 0
|
||||
this.phase = 1;
|
||||
this.lastPlayed = 0;
|
||||
|
@ -112,7 +130,9 @@ export default class Game {
|
|||
await this.giveup(); // The player who called BS won and now the challenged player must give up a card!
|
||||
return;
|
||||
}
|
||||
this.lastPlayed--;
|
||||
}
|
||||
this.phase = 3;
|
||||
await this.giveup(); // The player who called BS won and now they must give up a card!
|
||||
}
|
||||
async prepare() { // Players prepare their hand for this round
|
||||
|
@ -126,6 +146,7 @@ export default class Game {
|
|||
delete p.disconnectListener;
|
||||
(() => {
|
||||
p.stack = stack;
|
||||
p.cards = [...stack];
|
||||
return;
|
||||
})();
|
||||
resolve();
|
||||
|
@ -174,8 +195,9 @@ export default class Game {
|
|||
delete p.disconnectListener;
|
||||
(() => {
|
||||
if (this.players[selectedPlayer].stack.length > 0) {
|
||||
if (this.players[selectedPlayer].stack[0].suit === Suit.Diamonds ||
|
||||
this.players[selectedPlayer].stack[0].suit === Suit.Hearts) this.phase = 3; // Red card
|
||||
if (this.players[selectedPlayer].stack[0].suit === Suit.Spades ||
|
||||
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].stack.splice(0, 1);
|
||||
return;
|
||||
}
|
||||
|
@ -192,7 +214,7 @@ export default class Game {
|
|||
});
|
||||
}
|
||||
async giveup() { // Give up a card
|
||||
const p = this.players[this.lastPlayed ? this.lastPlayedPlayer : this.playerTurn];
|
||||
const p = this.players[this.lastPlayed > 0 ? this.lastPlayedPlayer : this.playerTurn];
|
||||
this.broadcastGameState();
|
||||
await new Promise<void>(resolve => {
|
||||
p.client.once('giveup', card => {
|
||||
|
|
|
@ -3,15 +3,4 @@ import http from 'http';
|
|||
import https from 'https';
|
||||
import {Server} from 'socket.io';
|
||||
|
||||
//export default new Server({cors: {origin: process.env.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!, () => {
|
||||
console.log(`Listening on port ${process.env.PORT}`);
|
||||
});
|
||||
|
||||
export default new Server(base, {cors: {origin: process.env.ORIGIN, methods: ['GET', 'POST']}});
|
||||
export default new Server(Number(process.env.PORT!), {cors: {origin: '*', methods: ['GET', 'POST']}});
|
||||
|
|
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",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bsx-core",
|
||||
"name": "mccarthyism-core",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
|
@ -13,9 +13,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
|
||||
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
|
||||
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
@ -28,9 +28,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"typescript": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
|
||||
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
|
||||
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "bsx-core",
|
||||
"name": "mccarthyism-core",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
enum Suit {
|
||||
Clubs,
|
||||
Diamonds,
|
||||
Hearts,
|
||||
Clubs,
|
||||
Spades
|
||||
}
|
||||
|
||||
|
|
5
front/next-env.d.ts
vendored
5
front/next-env.d.ts
vendored
|
@ -1,2 +1,5 @@
|
|||
/// <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.
|
||||
|
|
6245
front/package-lock.json
generated
6245
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",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -10,8 +10,8 @@
|
|||
"preinstall": "cd ../core && npm i && npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"bsx-core": "file:../core",
|
||||
"next": "10.0.6",
|
||||
"mccarthyism-core": "file:../core",
|
||||
"next": "^12.0.8",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.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 io from 'socket.io-client';
|
||||
|
||||
|
@ -8,7 +8,7 @@ import LoginForm from '../components/LoginForm';
|
|||
|
||||
interface GameState {
|
||||
cards: Card[],
|
||||
players: {username: string, numCards: number, stackSize: number, rank: number}[],
|
||||
players: {username: string, numCards: number, stackSize: number, flipped: Card[], rank: number}[],
|
||||
lastPlayed: number,
|
||||
lastPlayedPlayer: string | null,
|
||||
playerTurn: string
|
||||
|
@ -16,7 +16,21 @@ interface GameState {
|
|||
}
|
||||
|
||||
const rankStrs = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
|
||||
const suitChars = ['♣', '♦', '♥', '♠'];
|
||||
const suitChars = ['♦', '♥', '♣', '♠'];
|
||||
|
||||
const rules = `There are only 5 simple rules!
|
||||
|
||||
1. Each player will first be dealt the same number of cards.
|
||||
|
||||
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 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. 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!
|
||||
|
||||
`;
|
||||
|
||||
function useForceUpdate(){
|
||||
const [value, setValue] = useState(0);
|
||||
|
@ -71,14 +85,23 @@ export default function Game() {
|
|||
if (!socket) return null;
|
||||
if (!loggedIn) {
|
||||
return (
|
||||
<LoginForm
|
||||
socket={socket}
|
||||
finish={(s) => {
|
||||
setLoggedIn(true);
|
||||
setUsername(s);
|
||||
}}
|
||||
username={username}
|
||||
/>
|
||||
<>
|
||||
<h2>
|
||||
Welcome to McCarthyism!
|
||||
</h2>
|
||||
<div>
|
||||
{rules}
|
||||
</div>
|
||||
<LoginForm
|
||||
socket={socket}
|
||||
finish={(s) => {
|
||||
setLoggedIn(true);
|
||||
setUsername(s);
|
||||
}}
|
||||
username={username}
|
||||
/>
|
||||
This game is licensed under the AGPL. <a href="https://git.exozy.me/Ta180m/McCarthyism">Source code here!</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (!room) {
|
||||
|
@ -115,6 +138,16 @@ export default function Game() {
|
|||
if (gameState.phase === 0) {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => {
|
||||
document.getElementById('Rules')!.style.display = (document.getElementById('Rules')!.style.display === 'none' ? 'block' : 'none');
|
||||
}}
|
||||
>
|
||||
Show/hide rules
|
||||
</button>
|
||||
<div id='Rules' style={{display: 'none'}}>
|
||||
{rules}
|
||||
</div>
|
||||
<ul>
|
||||
{gameState.players.map(p => (
|
||||
<li key={p.username}>
|
||||
|
@ -122,9 +155,12 @@ export default function Game() {
|
|||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div>
|
||||
{gameState.playerTurn+` will go first this round!`}
|
||||
</div>
|
||||
{`Rearrange your card stack from top to bottom!`}
|
||||
<div>
|
||||
<p>Your cards:</p>
|
||||
<p>Your cards stack:</p>
|
||||
{gameState.cards.map((card, i) => (
|
||||
<label key={card.rank+' '+card.suit}>
|
||||
<div>
|
||||
|
@ -158,6 +194,16 @@ export default function Game() {
|
|||
if (gameState.phase === 1) {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => {
|
||||
document.getElementById('Rules')!.style.display = (document.getElementById('Rules')!.style.display === 'none' ? 'block' : 'none');
|
||||
}}
|
||||
>
|
||||
Show/hide rules
|
||||
</button>
|
||||
<div id='Rules' style={{display: 'none'}}>
|
||||
{rules}
|
||||
</div>
|
||||
<ul>
|
||||
{gameState.players.map(p => (
|
||||
<li key={p.username}>
|
||||
|
@ -201,6 +247,16 @@ export default function Game() {
|
|||
if (gameState.phase === 2) {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => {
|
||||
document.getElementById('Rules')!.style.display = (document.getElementById('Rules')!.style.display === 'none' ? 'block' : 'none');
|
||||
}}
|
||||
>
|
||||
Show/hide rules
|
||||
</button>
|
||||
<div id='Rules' style={{display: 'none'}}>
|
||||
{rules}
|
||||
</div>
|
||||
<ul>
|
||||
{gameState.players.map(p => (
|
||||
<li key={p.username}>
|
||||
|
@ -208,18 +264,27 @@ export default function Game() {
|
|||
</li>
|
||||
))}
|
||||
</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>
|
||||
<p>Stacks:</p>
|
||||
{gameState.players.map((player, i) => (
|
||||
<label key={player.username+': '+player.stackSize}>
|
||||
<button
|
||||
onClick={() => socket.emit('flip', i)}
|
||||
disabled={username !== gameState.lastPlayedPlayer}
|
||||
>
|
||||
Flip!
|
||||
</button>
|
||||
{' '+player.username+': '+player.stackSize+' cards '}
|
||||
<div>
|
||||
<button
|
||||
onClick={() => socket.emit('flip', i)}
|
||||
disabled={username !== gameState.lastPlayedPlayer || player.stackSize === 0}
|
||||
>
|
||||
Flip!
|
||||
</button>
|
||||
{' '+player.username+': '+player.stackSize+' cards '}
|
||||
{player.flipped.map((card, i) => (
|
||||
<label key={card.rank+' '+card.suit}>
|
||||
<span style={{color: (card.suit === Suit.Hearts || card.suit === Suit.Diamonds ? 'red' : 'black')}}>
|
||||
{' '+rankStrs[card.rank]+' '+suitChars[card.suit]}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
@ -229,6 +294,16 @@ export default function Game() {
|
|||
if (gameState.phase === 3) {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => {
|
||||
document.getElementById('Rules')!.style.display = (document.getElementById('Rules')!.style.display === 'none' ? 'block' : 'none');
|
||||
}}
|
||||
>
|
||||
Show/hide rules
|
||||
</button>
|
||||
<div id='Rules' style={{display: 'none'}}>
|
||||
{rules}
|
||||
</div>
|
||||
<ul>
|
||||
{gameState.players.map(p => (
|
||||
<li key={p.username}>
|
||||
|
@ -236,7 +311,30 @@ export default function Game() {
|
|||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{(gameState.lastPlayed ? `${gameState.lastPlayedPlayer}` : `${gameState.playerTurn}`) + ` lost! Now they must give up one of their cards!`}
|
||||
{(gameState.lastPlayed > 0 ? `${gameState.lastPlayedPlayer}` : `${gameState.playerTurn}`) + ` lost! Now they must give up one of their cards!`}
|
||||
<div>
|
||||
<p>Stacks:</p>
|
||||
{gameState.players.map((player, i) => (
|
||||
<label key={player.username+': '+player.stackSize}>
|
||||
<div>
|
||||
<button
|
||||
onClick={() => socket.emit('flip', i)}
|
||||
disabled={true}
|
||||
>
|
||||
Flip!
|
||||
</button>
|
||||
{' '+player.username+': '+player.stackSize+' cards '}
|
||||
{player.flipped.map((card, i) => (
|
||||
<label key={card.rank+' '+card.suit}>
|
||||
<span style={{color: (card.suit === Suit.Hearts || card.suit === Suit.Diamonds ? 'red' : 'black')}}>
|
||||
{' '+rankStrs[card.rank]+' '+suitChars[card.suit]}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<p>Your cards:</p>
|
||||
{gameState.cards.map((card, i) => (
|
||||
|
@ -244,7 +342,7 @@ export default function Game() {
|
|||
<div>
|
||||
<button
|
||||
onClick={() => socket.emit('giveup', i)}
|
||||
disabled={username !== (gameState.lastPlayed ? gameState.lastPlayedPlayer : gameState.playerTurn)}
|
||||
disabled={username !== (gameState.lastPlayed > 0 ? gameState.lastPlayedPlayer : gameState.playerTurn)}
|
||||
>
|
||||
Give up this card!
|
||||
</button>
|
||||
|
|
|
@ -14,3 +14,7 @@ a {
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
div {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
|
17
run
Executable file
17
run
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
# Environment variable
|
||||
export NEXT_PUBLIC_BACK_HOST='https://7.exozy.me'
|
||||
export FRONT_PORT=4206
|
||||
export PORT=4207
|
||||
|
||||
# Build
|
||||
cd front
|
||||
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