trucoshi 7.0.0-alpha.2 → 7.0.0-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,7 +48,7 @@ Proba la demo actual en [Trucoshi](https://trucoshi.com)
48
48
  [x] Unit tests
49
49
  [x] Bitcoin Lightning integration
50
50
  [x] Historial de partidas
51
- [ ] Cantar flor
51
+ [x] Cantar flor
52
52
  [ ] Torneos
53
53
 
54
54
  # Donations
@@ -1,4 +1,4 @@
1
- import { BURNT_CARD, CARDS } from "../../types";
1
+ import { BURNT_CARD, CARDS } from "../constants";
2
2
  import { Random } from "./Random";
3
3
  export function Deck() {
4
4
  const deck = {
@@ -37,6 +37,8 @@ export function Deck() {
37
37
  export const getAllCards = () => Object.keys(CARDS);
38
38
  export function dealCards(table, deck) {
39
39
  const cheat_lots_of_flowers = process.env.APP_CHEAT_LOTS_OF_FLOWERS_FOR_TESTING === "1";
40
+ const cheat_cards = process.env.APP_CHEAT_CARDS &&
41
+ JSON.parse(process.env.APP_CHEAT_CARDS);
40
42
  const playerHands = [];
41
43
  const players = table.getPlayersForehandFirst();
42
44
  for (let i = 0; i < 3; i++) {
@@ -44,13 +46,25 @@ export function dealCards(table, deck) {
44
46
  playerHands[player.idx] = [...(playerHands[player.idx] || []), deck.takeCard()];
45
47
  }
46
48
  }
47
- if (cheat_lots_of_flowers && table.forehandIdx % 2 === 0) {
49
+ if (cheat_cards) {
50
+ deck.shuffle(players[0].idx);
51
+ for (const [key, player] of players.entries()) {
52
+ playerHands[player.idx] = cheat_cards[key]
53
+ ? cheat_cards[key].map((c) => deck.pick(c) || deck.takeCard())
54
+ : deck.takeThree();
55
+ }
56
+ }
57
+ if (cheat_lots_of_flowers) {
48
58
  deck.shuffle(players[0].idx);
49
59
  for (const player of players) {
50
- const first = deck.takeCard();
51
- const second = deck.pick(deck.cards.find((c) => c.charAt(1) === first.charAt(1)));
52
- const third = deck.pick(deck.cards.find((c) => c.charAt(1) === first.charAt(1)));
53
- playerHands[player.idx] = [first, second, third];
60
+ if (Math.random() > 0.5) {
61
+ const first = deck.takeCard();
62
+ const second = deck.pick(deck.cards.find((c) => c.charAt(1) === first.charAt(1)));
63
+ const third = deck.pick(deck.cards.find((c) => c.charAt(1) === first.charAt(1)));
64
+ playerHands[player.idx] = [first, second, third];
65
+ continue;
66
+ }
67
+ playerHands[player.idx] = deck.takeThree();
54
68
  }
55
69
  }
56
70
  for (const [key, player] of table.players.entries()) {
@@ -1,5 +1,5 @@
1
1
  export interface IQueue {
2
2
  promise: Promise<void>;
3
- queue(operation: () => any): Promise<void>;
3
+ queue<T>(operation: () => Promise<T>): Promise<T>;
4
4
  }
5
5
  export declare const Queue: () => IQueue;
@@ -1,15 +1,16 @@
1
1
  import logger from "../../utils/logger";
2
+ const log = logger.child({ class: "Queue" });
2
3
  export const Queue = () => {
3
4
  const queue = {
4
5
  promise: Promise.resolve(),
5
6
  queue(operation) {
6
- return new Promise((resolve) => {
7
+ return new Promise((resolve, reject) => {
7
8
  queue.promise = queue.promise
8
9
  .then(operation)
9
10
  .then(resolve)
10
11
  .catch((e) => {
11
- logger.error(e, "Error in queue operation %o", operation);
12
- resolve();
12
+ log.error(e, "Error in queue operation %o", operation);
13
+ reject(e);
13
14
  });
14
15
  });
15
16
  },
@@ -1,34 +1,48 @@
1
+ import logger from "../../utils/logger";
2
+ const log = logger.child({ class: "Table" });
1
3
  export function Table(players) {
4
+ log.trace({ playerKeys: players.map((p) => p.key) }, "Creating new table");
2
5
  const table = {
3
6
  players,
4
7
  cards: [],
5
8
  forehandIdx: 0,
6
9
  nextHand() {
10
+ log.trace({ currentForehandIdx: table.forehandIdx }, "nextHand called");
7
11
  if (table.forehandIdx < table.players.length - 1) {
8
12
  table.forehandIdx++;
9
13
  }
10
14
  else {
11
15
  table.forehandIdx = 0;
12
16
  }
13
- return table.getPlayerByPosition();
17
+ log.trace({ newForehandIdx: table.forehandIdx }, "nextHand updated");
18
+ const nextPlayer = table.getPlayerByPosition();
19
+ log.trace({ playerKey: nextPlayer.key }, "nextHand returning player");
20
+ return nextPlayer;
14
21
  },
15
22
  getPlayerPosition(key, forehandFirst = false) {
23
+ log.trace({ key, forehandFirst }, "getPlayerPosition called");
16
24
  const array = forehandFirst ? table.getPlayersForehandFirst() : table.players;
17
- return array.findIndex((p) => p.key === key);
25
+ const position = array.findIndex((p) => p.key === key);
26
+ log.trace({ key, position }, "getPlayerPosition result");
27
+ return position;
18
28
  },
19
29
  getPlayersForehandFirst(forehand) {
20
30
  const idx = forehand !== undefined ? forehand : table.forehandIdx;
31
+ log.trace({ forehandIdx: idx }, "getPlayersForehandFirst called");
21
32
  const cut = players.slice(idx, table.players.length);
22
33
  const end = players.slice(0, idx);
23
- return cut.concat(end);
34
+ const result = cut.concat(end);
35
+ log.trace({ playerKeys: result.map((p) => p.key) }, "getPlayersForehandFirst result");
36
+ return result;
24
37
  },
25
38
  getPlayerByPosition(idx, forehandFirst = false) {
39
+ log.trace({ idx, forehandFirst }, "getPlayerByPosition called");
26
40
  const array = forehandFirst ? table.getPlayersForehandFirst() : table.players;
27
- if (idx !== undefined) {
28
- return array[idx];
29
- }
30
- return array[0];
41
+ const player = idx !== undefined ? array[idx] : array[0];
42
+ log.trace({ playerKey: player.key }, "getPlayerByPosition result");
43
+ return player;
31
44
  },
32
45
  };
46
+ log.trace({ forehandIdx: table.forehandIdx }, "Table initialization complete");
33
47
  return table;
34
48
  }
@@ -1,6 +1,5 @@
1
1
  import { IRound } from "../truco";
2
- import { ICard, IPlayer } from "../types";
3
- export declare function calculateFlorPoints(player: IPlayer): number;
2
+ import { ICard } from "../types";
4
3
  export declare function getMaxNumberIndex<T = number>(array: Array<T>): number;
5
4
  export declare function getMinNumberIndex<T = number>(array: Array<T>): number;
6
5
  export declare function getCardValue(card: ICard): number;
package/dist/lib/utils.js CHANGED
@@ -1,21 +1,4 @@
1
- import { splitCardvalues } from "../truco";
2
1
  import { CARDS } from "./constants";
3
- // Calculates the Flor points for a player's hand.
4
- // Returns the sum of the envidoValue of three cards of the same suit plus 20, or 0 if no Flor exists.
5
- export function calculateFlorPoints(player) {
6
- if (!player.hasFlor) {
7
- return 0;
8
- }
9
- const hand = [...player.hand, ...player.usedHand].map(splitCardvalues);
10
- // Verify that all cards share the same suit (should be true if player.hasFlor is set)
11
- const sameSuit = hand.every((card) => card.palo === hand[0].palo);
12
- if (!sameSuit) {
13
- return 0;
14
- }
15
- // Sum the envidoValue of all cards (figures are 0)
16
- const points = hand.reduce((sum, card) => sum + card.value, 20);
17
- return points;
18
- }
19
2
  export function getMaxNumberIndex(array) {
20
3
  return array.reduce((accumulator, current, index) => {
21
4
  return current > array[accumulator] ? index : accumulator;
package/dist/types.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  export * from "./events";
2
2
  export { CARDS, CARDS_HUMAN_READABLE, BURNT_CARD } from "./lib/constants";
3
3
  import { User } from "lightning-accounts";
4
- import { CARDS } from "./lib";
5
4
  import { Match, MatchPlayer, MatchHand, UserStats } from "@trucoshi/prisma";
6
5
  import { IHand } from "./truco";
6
+ import { CARDS } from "./lib";
7
7
  export declare enum EMatchState {
8
8
  UNREADY = "UNREADY",
9
9
  READY = "READY",
@@ -39,6 +39,16 @@ export interface ISaidCommand {
39
39
  player: IPlayer | IPublicPlayer;
40
40
  command: ECommand | number;
41
41
  }
42
+ export interface IMatchFlorBattle {
43
+ matchSessionId: string;
44
+ playersWithFlor: {
45
+ idx: number;
46
+ cards?: ICard[];
47
+ points: number;
48
+ }[];
49
+ winnerTeamIdx: 0 | 1 | null;
50
+ winner: IPublicPlayer | null;
51
+ }
42
52
  export interface IMatchPreviousHand {
43
53
  envido: {
44
54
  winner: IPublicPlayer;
@@ -48,6 +58,7 @@ export interface IMatchPreviousHand {
48
58
  };
49
59
  } | null;
50
60
  flor: {
61
+ winner: IPublicPlayer | null;
51
62
  data: Array<{
52
63
  idx: number;
53
64
  value: number;
@@ -64,8 +75,10 @@ export interface IPublicMatch {
64
75
  busy: boolean;
65
76
  state: EMatchState;
66
77
  handState: EHandState | null;
78
+ florBattle: IMatchFlorBattle | null;
67
79
  winner: ITeam | null;
68
80
  matchSessionId: string;
81
+ forehandIdx: number;
69
82
  ownerKey: string;
70
83
  teams: Array<IPublicTeam>;
71
84
  players: Array<IPublicPlayer>;
@@ -89,11 +102,13 @@ export interface IChatMessage {
89
102
  user: {
90
103
  name: string;
91
104
  key: string;
105
+ teamIdx?: 0 | 1;
92
106
  };
93
107
  system?: boolean;
94
108
  command?: boolean;
95
109
  card?: boolean;
96
110
  content: string;
111
+ sound: string | boolean;
97
112
  }
98
113
  export interface IChatRoom {
99
114
  id: string;
@@ -101,10 +116,10 @@ export interface IChatRoom {
101
116
  socket: {
102
117
  emit(socket: string): void;
103
118
  };
104
- send(user: IChatMessage["user"], message: string): void;
105
- card(user: IChatMessage["user"], card: ICard): void;
106
- command(team: 0 | 1, command: ECommand | number): void;
107
- system(message: string): void;
119
+ send(user: IChatMessage["user"], message: string, sound?: string | boolean): void;
120
+ card(user: IChatMessage["user"], card: ICard, sound?: string | boolean): void;
121
+ command(team: 0 | 1, command: ECommand | number, sound?: string | boolean): void;
122
+ system(message: string, sound?: string | boolean): void;
108
123
  emit(message?: IChatMessage): void;
109
124
  }
110
125
  export declare enum EChatSystem {
@@ -134,9 +149,9 @@ export declare enum EEnvidoAnswerCommand {
134
149
  SON_BUENAS = "SON_BUENAS"
135
150
  }
136
151
  export declare enum EEnvidoCommand {
137
- ENVIDO = "ENVIDO",
152
+ FALTA_ENVIDO = "FALTA_ENVIDO",
138
153
  REAL_ENVIDO = "REAL_ENVIDO",
139
- FALTA_ENVIDO = "FALTA_ENVIDO"
154
+ ENVIDO = "ENVIDO"
140
155
  }
141
156
  export declare enum EHandState {
142
157
  WAITING_PLAY = "WAITING_PLAY",
@@ -144,6 +159,7 @@ export declare enum EHandState {
144
159
  WAITING_ENVIDO_ANSWER = "WAITING_ENVIDO_ANSWER",
145
160
  WAITING_ENVIDO_POINTS_ANSWER = "WAITING_ENVIDO_POINTS_ANSWER",
146
161
  WAITING_FLOR_ANSWER = "WAITING_FLOR_ANSWER",
162
+ DISPLAY_FLOR_BATTLE = "DISPLAY_FLOR_BATTLE",
147
163
  BEFORE_FINISHED = "BEFORE_FINISHED",
148
164
  FINISHED = "FINISHED"
149
165
  }
@@ -248,10 +264,10 @@ export type IPublicTeam = Pick<ITeam, "points" | "id" | "name"> & {
248
264
  export interface IPlayer {
249
265
  idx: number;
250
266
  secret: string;
251
- teamIdx: number;
267
+ teamIdx: 0 | 1;
252
268
  accountId: number | undefined;
253
269
  matchPlayerId: number | undefined;
254
- avatarUrl: string | undefined;
270
+ avatarUrl: string | undefined | null;
255
271
  name: string;
256
272
  key: string;
257
273
  session: string;
@@ -270,15 +286,21 @@ export interface IPlayer {
270
286
  turnExpiresAt: number | null;
271
287
  turnExtensionExpiresAt: number | null;
272
288
  hasFlor: boolean;
273
- hasSaidFlorPoints: boolean;
289
+ flor: {
290
+ value: number;
291
+ cards: ICard[];
292
+ } | null;
293
+ hasSaidFlor: boolean;
274
294
  hasSaidEnvidoPoints: boolean;
295
+ hasSaidTruco: boolean;
275
296
  isEnvidoTurn: boolean;
276
297
  isOwner: boolean;
277
298
  disabled: boolean;
278
299
  abandoned: boolean;
279
300
  ready: boolean;
280
301
  saidEnvidoPoints(): void;
281
- saidFlorPoints(): void;
302
+ saidFlor(): void;
303
+ saidTruco(): void;
282
304
  resetCommands(): void;
283
305
  calculateEnvido(): Array<{
284
306
  value: number;
@@ -307,6 +329,7 @@ export interface ITeam {
307
329
  name: string;
308
330
  players: Array<IPlayer>;
309
331
  points: ITeamPoints;
332
+ setPlayers(players: IPlayer[]): ITeam;
310
333
  pointsToWin(matchPoint: number): number;
311
334
  getPublicTeam(playerSession?: string): IPublicTeam;
312
335
  isTeamDisabled(): boolean;
package/dist/types.js CHANGED
@@ -41,9 +41,9 @@ export var EEnvidoAnswerCommand;
41
41
  })(EEnvidoAnswerCommand || (EEnvidoAnswerCommand = {}));
42
42
  export var EEnvidoCommand;
43
43
  (function (EEnvidoCommand) {
44
- EEnvidoCommand["ENVIDO"] = "ENVIDO";
45
- EEnvidoCommand["REAL_ENVIDO"] = "REAL_ENVIDO";
46
44
  EEnvidoCommand["FALTA_ENVIDO"] = "FALTA_ENVIDO";
45
+ EEnvidoCommand["REAL_ENVIDO"] = "REAL_ENVIDO";
46
+ EEnvidoCommand["ENVIDO"] = "ENVIDO";
47
47
  })(EEnvidoCommand || (EEnvidoCommand = {}));
48
48
  export var EHandState;
49
49
  (function (EHandState) {
@@ -52,6 +52,7 @@ export var EHandState;
52
52
  EHandState["WAITING_ENVIDO_ANSWER"] = "WAITING_ENVIDO_ANSWER";
53
53
  EHandState["WAITING_ENVIDO_POINTS_ANSWER"] = "WAITING_ENVIDO_POINTS_ANSWER";
54
54
  EHandState["WAITING_FLOR_ANSWER"] = "WAITING_FLOR_ANSWER";
55
+ EHandState["DISPLAY_FLOR_BATTLE"] = "DISPLAY_FLOR_BATTLE";
55
56
  EHandState["BEFORE_FINISHED"] = "BEFORE_FINISHED";
56
57
  EHandState["FINISHED"] = "FINISHED";
57
58
  })(EHandState || (EHandState = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trucoshi",
3
- "version": "7.0.0-alpha.2",
3
+ "version": "7.0.0-rc.11",
4
4
  "description": "Lightning Truco Server",
5
5
  "main": "dist/types.js",
6
6
  "license": "GPL-3.0",
@@ -33,8 +33,8 @@
33
33
  "test:e2e": "prisma db push --force-reset && dotenv -e .env.test -- ts-mocha --exit ./test/**/*.ts -t 60000",
34
34
  "profiler": "node --prof ./bin/trucoshi-server",
35
35
  "prepublishOnly": "yarn test && yarn build:dist",
36
- "cli:autoplay": "yarn run ts-node src/cli/autoplay",
37
- "cli:play": "yarn run ts-node src/cli/play",
36
+ "cli:autoplay": "APP_DEBUG_LEVEL=warn yarn run ts-node src/utils/scripts/autoplay",
37
+ "cli:play": "APP_DEBUG_LEVEL=warn yarn run ts-node src/utils/scripts/play",
38
38
  "db:push": "prisma db push",
39
39
  "db:studio": "prisma studio",
40
40
  "db:migrate": "prisma migrate dev"
@@ -1,3 +0,0 @@
1
- export declare const PLAYER_TIMEOUT_GRACE = 500;
2
- export declare const PLAYER_LOBBY_TIMEOUT: number;
3
- export declare const MATCH_FINISHED_CLEANUP_TIMEOUT: number;
@@ -1,3 +0,0 @@
1
- export const PLAYER_TIMEOUT_GRACE = 500;
2
- export const PLAYER_LOBBY_TIMEOUT = 1000 * 10;
3
- export const MATCH_FINISHED_CLEANUP_TIMEOUT = 1000 * 60 * 2;