trucoshi 0.0.8 → 0.0.10

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/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "trucoshi",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "main": "build/lib/trucoshi.js",
5
5
  "license": "GPL-3.0",
6
6
  "scripts": {
7
- "prepare": "yarn build",
7
+ "prepublishOnly": "yarn build",
8
8
  "dev": "nodemon",
9
9
  "test:autoplay": "yarn run ts-node src/test/autoplay",
10
10
  "test:play": "yarn run ts-node src/test/play",
@@ -15,7 +15,9 @@
15
15
  "@types/node": "^18.11.18",
16
16
  "nodemon": "^2.0.20"
17
17
  },
18
-
18
+ "files": [
19
+ "build"
20
+ ],
19
21
  "dependencies": {
20
22
  "rimraf": "^4.1.1",
21
23
  "socket.io": "^4.5.4",
package/.prettierrc.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "trailingComma": "es5",
3
- "tabWidth": 2,
4
- "semi": false,
5
- "singleQuote": false,
6
- "printWidth": 100
7
- }
package/nodemon.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "watch": ["src"],
3
- "ext": ".ts,.js",
4
- "ignore": [],
5
- "exec": "yarn run ts-node ./src/server/index.ts"
6
- }
@@ -1,25 +0,0 @@
1
- import { CARDS } from "../constants"
2
- import { ICard, IDeck } from "../types"
3
- import { shuffleArray } from "../utils"
4
-
5
- export function Deck(): IDeck {
6
- const deck: IDeck = {
7
- cards: Object.keys(CARDS) as Array<ICard>,
8
- usedCards: [],
9
- takeCard() {
10
- const card = deck.cards.shift() as ICard
11
- deck.usedCards.push(card)
12
- return card
13
- },
14
- shuffle() {
15
- deck.cards = deck.cards.concat(deck.usedCards)
16
- deck.usedCards = []
17
- deck.cards = shuffleArray(deck.cards)
18
- if (deck.cards.length !== 40) {
19
- throw new Error("This is not good")
20
- }
21
- return deck
22
- },
23
- }
24
- return deck
25
- }
@@ -1,207 +0,0 @@
1
- import {
2
- EEnvidoCommand,
3
- EHandState,
4
- ESayCommand,
5
- IDeck,
6
- IHand,
7
- IHandCommands,
8
- IMatch,
9
- } from "../types"
10
- import { checkHandWinner } from "../utils"
11
- import { PlayInstance } from "./Play"
12
- import { Round } from "./Round"
13
- import { Truco } from "./Truco"
14
-
15
- export function Hand(match: IMatch, deck: IDeck, idx: number) {
16
- match.teams.forEach((team) => {
17
- team.players.forEach((player) => {
18
- const playerHand = [deck.takeCard(), deck.takeCard(), deck.takeCard()]
19
- player.setHand(playerHand)
20
- player.enable()
21
- // player.setHand(["5c", "4c", "6c"])
22
- })
23
- })
24
-
25
- function* roundsGeneratorSequence() {
26
- let currentRoundIdx = 0
27
- let forehandTeamIdx = match.table.player(hand.turn).teamIdx as 0 | 1
28
-
29
- while (currentRoundIdx < 3 && !hand.finished()) {
30
- const round = Round(0)
31
- hand.setCurrentRound(round)
32
- hand.pushRound(round)
33
-
34
- let previousRound = hand.rounds[currentRoundIdx - 1]
35
-
36
- // Put previous round winner as forehand
37
- if (previousRound && previousRound.winner && !previousRound.tie) {
38
- const newTurn = match.table.getPlayerPosition(previousRound.winner.id)
39
- if (newTurn !== -1) {
40
- hand.setTurn(newTurn)
41
- }
42
- }
43
-
44
- while (round.turn < match.table.players.length) {
45
-
46
- while (hand.state === EHandState.WAITING_FOR_TRUCO_ANSWER) {
47
- const { value } = hand.truco.getNextPlayer()
48
- if (value && value.currentPlayer) {
49
- console.log({ value: value.currentPlayer })
50
- hand.setCurrentPlayer(value.currentPlayer)
51
- yield hand
52
- }
53
- }
54
-
55
- const player = match.table.player(hand.turn)
56
- hand.setCurrentPlayer(player)
57
- if (player.disabled) {
58
- hand.setCurrentPlayer(null)
59
- }
60
-
61
- yield hand
62
- }
63
-
64
- if (match.teams[0].isTeamDisabled() && match.teams[1].isTeamDisabled()) {
65
- hand.setState(EHandState.FINISHED)
66
- break
67
- }
68
-
69
- let winnerTeamIdx = checkHandWinner(hand.rounds, forehandTeamIdx)
70
-
71
- if (match.teams[0].isTeamDisabled()) {
72
- winnerTeamIdx = 1
73
- }
74
- if (match.teams[1].isTeamDisabled()) {
75
- winnerTeamIdx = 0
76
- }
77
-
78
- if (winnerTeamIdx !== null) {
79
- hand.addPoints(winnerTeamIdx, hand.truco.state)
80
- hand.setState(EHandState.FINISHED)
81
- }
82
-
83
- currentRoundIdx++
84
- }
85
- yield hand
86
- }
87
-
88
- const roundsGenerator = roundsGeneratorSequence()
89
-
90
- const commands: IHandCommands = {
91
- [ESayCommand.MAZO]: (player) => {
92
- hand.disablePlayer(player)
93
- },
94
- [ESayCommand.TRUCO]: (player) => {
95
- const { teamIdx } = hand.truco
96
- if (teamIdx === null || teamIdx !== player.teamIdx) {
97
- hand.setState(EHandState.WAITING_FOR_TRUCO_ANSWER)
98
- hand.truco.sayTruco(player.teamIdx as 0 | 1, match.teams[Number(!player.teamIdx)].players)
99
- }
100
- },
101
- [ESayCommand.QUIERO]: () => {
102
- if (hand.state === EHandState.WAITING_FOR_TRUCO_ANSWER) {
103
- hand.truco.setAnswer(true)
104
- hand.setState(EHandState.WAITING_PLAY)
105
- }
106
- },
107
- [ESayCommand.NO_QUIERO]: (player) => {
108
- if (hand.state === EHandState.WAITING_FOR_TRUCO_ANSWER) {
109
- hand.truco.setAnswer(false)
110
- hand.setState(EHandState.WAITING_PLAY)
111
- }
112
- },
113
- [ESayCommand.FLOR]: () => {},
114
- [ESayCommand.CONTRAFLOR]: () => {},
115
- [EEnvidoCommand.ENVIDO]: () => {},
116
- [EEnvidoCommand.ENVIDO_ENVIDO]: () => {},
117
- [EEnvidoCommand.REAL_ENVIDO]: () => {},
118
- [EEnvidoCommand.FALTA_ENVIDO]: () => {},
119
- }
120
-
121
- const hand: IHand = {
122
- idx,
123
- turn: Number(match.table.forehandIdx),
124
- state: EHandState.WAITING_PLAY,
125
- rounds: [],
126
- truco: Truco(),
127
- envido: {
128
- accept: 1,
129
- decline: 2,
130
- teamIdx: null,
131
- },
132
- points: [0, 0],
133
- currentRound: null,
134
- _currentPlayer: null,
135
- set currentPlayer(player) {
136
- hand._currentPlayer = player
137
- },
138
- get currentPlayer() {
139
- if (hand.state === EHandState.WAITING_FOR_TRUCO_ANSWER) {
140
- return hand.truco.currentPlayer
141
- }
142
- return hand._currentPlayer
143
- },
144
- commands,
145
- play() {
146
- return PlayInstance(hand, match.teams)
147
- },
148
- use(idx: number) {
149
- const player = hand.currentPlayer
150
- const round = hand.currentRound
151
- if (!player || !round) {
152
- return null
153
- }
154
-
155
- const card = player.useCard(idx)
156
- if (card) {
157
- hand.nextTurn()
158
- return round.use({ player, card })
159
- }
160
-
161
- return null
162
- },
163
- nextTurn() {
164
- if (hand.turn >= match.table.players.length - 1) {
165
- hand.setTurn(0)
166
- } else {
167
- hand.setTurn(hand.turn + 1)
168
- }
169
-
170
- hand.currentRound?.nextTurn()
171
- },
172
- getNextPlayer() {
173
- return roundsGenerator.next()
174
- },
175
- disablePlayer(player) {
176
- match.teams[player.teamIdx].disable(player)
177
- },
178
- addPoints(team, points) {
179
- hand.points[team] = hand.points[team] + points
180
- },
181
- pushRound(round) {
182
- hand.rounds.push(round)
183
- return round
184
- },
185
- setTurn(turn) {
186
- hand.turn = turn
187
- return match.table.player(hand.turn)
188
- },
189
- setCurrentRound(round) {
190
- hand.currentRound = round
191
- return hand.currentRound
192
- },
193
- setCurrentPlayer(player) {
194
- hand._currentPlayer = player
195
- return hand._currentPlayer
196
- },
197
- setState(state) {
198
- hand.state = state
199
- return hand.state
200
- },
201
- finished: () => {
202
- return hand.state === EHandState.FINISHED
203
- },
204
- }
205
-
206
- return hand
207
- }
@@ -1,79 +0,0 @@
1
- import { IHand, IMatch, ITable, ITeam } from "../types"
2
- import { Deck } from "./Deck"
3
- import { Hand } from "./Hand"
4
-
5
- export function Match(table: ITable, teams: Array<ITeam> = [], matchPoint: number = 9): IMatch {
6
- const deck = Deck().shuffle()
7
-
8
- const size = teams[0].players.length
9
-
10
- if (size !== teams[1].players.length) {
11
- throw new Error("Team size mismatch")
12
- }
13
-
14
- function* handsGeneratorSequence() {
15
- while (!match.winner) {
16
- deck.shuffle()
17
- const hand = match.setCurrentHand(Hand(match, deck, match.hands.length + 1)) as IHand
18
- match.pushHand(hand)
19
- while (!hand.finished()) {
20
- const { value } = hand.getNextPlayer()
21
- if (value && value.finished()) {
22
- continue
23
- }
24
- match.setCurrentHand(value as IHand)
25
- yield match
26
- }
27
-
28
- match.setCurrentHand(null)
29
-
30
- const teams = match.addPoints(hand.points)
31
- const winner = teams.find((team) => team.points.winner)
32
-
33
- if (winner) {
34
- match.setWinner(winner)
35
- match.setCurrentHand(null)
36
- break
37
- }
38
- match.table.nextTurn()
39
- }
40
- yield match
41
- }
42
-
43
- const handsGenerator = handsGeneratorSequence()
44
-
45
- const match: IMatch = {
46
- winner: null,
47
- teams: teams as [ITeam, ITeam],
48
- hands: [],
49
- table,
50
- currentHand: null,
51
- play() {
52
- match.getNextTurn()
53
- if (!match.currentHand) {
54
- return null
55
- }
56
- return match.currentHand.play()
57
- },
58
- addPoints(points) {
59
- match.teams[0].addPoints(matchPoint, points[0])
60
- match.teams[1].addPoints(matchPoint, points[1])
61
- return match.teams
62
- },
63
- pushHand(hand) {
64
- match.hands.push(hand)
65
- },
66
- setCurrentHand(hand) {
67
- match.currentHand = hand
68
- return match.currentHand
69
- },
70
- setWinner(winner) {
71
- match.winner = winner
72
- },
73
- getNextTurn() {
74
- return handsGenerator.next()
75
- },
76
- }
77
-
78
- return match
79
- }
@@ -1,40 +0,0 @@
1
- import { EEnvidoCommand, EHandState, ESayCommand, IHand, IPlayInstance, ITeam } from "../types"
2
-
3
- export function PlayInstance(hand: IHand, teams: [ITeam, ITeam]) {
4
- const instance: IPlayInstance = {
5
- state: hand.state,
6
- teams,
7
- truco: hand.truco,
8
- envido: hand.envido,
9
- handIdx: hand.idx,
10
- roundIdx: hand.rounds.length,
11
- player: hand.currentPlayer,
12
- commands: [],
13
- rounds: hand.rounds,
14
- use(idx) {
15
- return hand.use(idx)
16
- },
17
- say(command) {
18
- if (!hand._currentPlayer || !instance.commands?.includes(command)) {
19
- return null
20
- }
21
-
22
- hand.commands[command](hand._currentPlayer)
23
-
24
- return command
25
- },
26
- }
27
-
28
- instance.commands?.push(ESayCommand.MAZO)
29
- instance.commands?.push(ESayCommand.TRUCO)
30
-
31
- if (hand.rounds.length === 1) {
32
- instance.commands?.push(EEnvidoCommand.ENVIDO)
33
- }
34
-
35
- if (hand.state === EHandState.WAITING_FOR_TRUCO_ANSWER) {
36
- instance.commands = [ESayCommand.TRUCO, ESayCommand.QUIERO, ESayCommand.NO_QUIERO]
37
- }
38
-
39
- return instance
40
- }
@@ -1,37 +0,0 @@
1
- import { IPlayer } from "../types"
2
-
3
- export function Player(id: string, teamIdx: number) {
4
- const player: IPlayer = {
5
- id,
6
- teamIdx,
7
- hand: [],
8
- commands: [],
9
- usedHand: [],
10
- disabled: false,
11
- ready: false,
12
- enable() {
13
- player.disabled = false
14
- },
15
- disable() {
16
- player.disabled = true
17
- },
18
- setReady(ready) {
19
- player.ready = ready
20
- },
21
- setHand(hand) {
22
- player.hand = hand
23
- player.usedHand = []
24
- return hand
25
- },
26
- useCard(idx) {
27
- if (player.hand[idx]) {
28
- const card = player.hand.splice(idx, 1)[0]
29
- player.usedHand.push(card)
30
- return card
31
- }
32
- return null
33
- },
34
- }
35
-
36
- return player
37
- }
@@ -1,30 +0,0 @@
1
- import { IRound } from "../types"
2
- import { getCardValue } from "../utils"
3
-
4
- export function Round(turn: number): IRound {
5
- const round: IRound = {
6
- turn,
7
- highest: -1,
8
- winner: null,
9
- cards: [],
10
- tie: false,
11
- nextTurn() {
12
- round.turn++
13
- },
14
- use({ card, player }) {
15
- const value = getCardValue(card)
16
- if (value === round.highest && player.teamIdx !== round.winner?.teamIdx) {
17
- round.tie = true
18
- }
19
- if (value > round.highest) {
20
- round.tie = false
21
- round.highest = value
22
- round.winner = player
23
- }
24
- round.cards.push({ card, player })
25
- return card
26
- },
27
- }
28
-
29
- return round
30
- }
@@ -1,32 +0,0 @@
1
- import { GAME_ERROR, TEAM_SIZE_VALUES } from "../constants"
2
- import { IPlayer, ITable, ITeam } from "../types"
3
- import { Match } from "./Match"
4
- import { Player } from "./Player"
5
- import { Team } from "./Team"
6
-
7
- export function Table(players: Array<IPlayer>, teams: Array<ITeam>): ITable {
8
- const table: ITable = {
9
- players,
10
- cards: [],
11
- forehandIdx: 0,
12
- nextTurn() {
13
- if (table.forehandIdx < table.players.length - 1) {
14
- table.forehandIdx++
15
- } else {
16
- table.forehandIdx = 0
17
- }
18
- return table.player()
19
- },
20
- getPlayerPosition(id) {
21
- return table.players.findIndex((p) => p.id === id)
22
- },
23
- player(idx) {
24
- if (idx !== undefined) {
25
- return table.players[idx]
26
- }
27
- return table.players[table.forehandIdx]
28
- },
29
- }
30
-
31
- return table
32
- }
@@ -1,41 +0,0 @@
1
- import { IPlayer, ITeam } from "../types"
2
-
3
- export function Team(players: Array<IPlayer>) {
4
- const team: ITeam = {
5
- _players: new Map<string, IPlayer>(),
6
- get players() {
7
- return Array.from(team._players.values())
8
- },
9
- points: {
10
- buenas: 0,
11
- malas: 0,
12
- winner: false,
13
- },
14
- isTeamDisabled() {
15
- return team.players.reduce((prev, curr) => prev && curr.disabled, true)
16
- },
17
- disable(player) {
18
- team._players.get(player.id)?.disable()
19
- return team.isTeamDisabled()
20
- },
21
- addPoints(matchPoint, points) {
22
- const malas = team.points.malas + points
23
- const diff = malas - matchPoint
24
- if (diff > 0) {
25
- team.points.malas = matchPoint
26
- team.points.buenas += diff
27
- if (team.points.buenas >= matchPoint) {
28
- team.points.winner = true
29
- }
30
- } else {
31
- team.points.malas = malas
32
- }
33
-
34
- return team.points
35
- },
36
- }
37
-
38
- players.forEach((player) => team._players.set(player.id, player))
39
-
40
- return team
41
- }
@@ -1,72 +0,0 @@
1
- import { ITruco } from "../types"
2
-
3
- export function Truco() {
4
- function* trucoAnswerGeneratorSequence() {
5
- let i = 0
6
- while (i < truco.players.length && truco.answer === null) {
7
- const player = truco.players[truco.turn]
8
- truco.setCurrentPlayer(player)
9
- if (player.disabled) {
10
- truco.setCurrentPlayer(null)
11
- }
12
-
13
- if (truco.turn >= truco.players.length - 1) {
14
- truco.setTurn(0)
15
- } else {
16
- truco.setTurn(truco.turn + 1)
17
- }
18
-
19
- i++
20
-
21
- yield truco
22
- }
23
- yield truco
24
- }
25
-
26
- const truco: ITruco = {
27
- turn: 0,
28
- state: 1,
29
- teamIdx: null,
30
- answer: null,
31
- currentPlayer: null,
32
- generator: trucoAnswerGeneratorSequence(),
33
- players: [],
34
- sayTruco(teamIdx, players) {
35
- truco.teamIdx = teamIdx
36
- truco.answer = null
37
- truco.players = players
38
- truco.generator = trucoAnswerGeneratorSequence()
39
- return truco
40
- },
41
- setPlayers(players) {
42
- truco.players = players
43
- },
44
- setAnswer(answer) {
45
- if (answer) {
46
- truco.state++
47
- }
48
- if (answer !== null) {
49
- truco.teamIdx = null
50
- }
51
- truco.answer = answer
52
- return truco
53
- },
54
- setTeam(idx: 0 | 1) {
55
- truco.teamIdx = idx
56
- return truco.teamIdx
57
- },
58
- setTurn(turn) {
59
- truco.turn = turn
60
- return truco.turn
61
- },
62
- setCurrentPlayer(player) {
63
- truco.currentPlayer = player
64
- return truco.currentPlayer
65
- },
66
- getNextPlayer() {
67
- return truco.generator.next()
68
- },
69
- }
70
-
71
- return truco
72
- }
@@ -1,108 +0,0 @@
1
- import {
2
- EEnvidoCommand,
3
- ESayCommand,
4
- IEnvidoCalculator,
5
- IEnvidoCalculatorArgs,
6
- ITeam,
7
- TeamPoints,
8
- } from "./types"
9
- import { getMaxNumberIndex } from "./utils"
10
-
11
- export const CARDS = {
12
- "1e": 14,
13
- "1b": 13,
14
- "7e": 12,
15
- "7o": 11,
16
- "3e": 10,
17
- "3o": 10,
18
- "3b": 10,
19
- "3c": 10,
20
- "2e": 9,
21
- "2o": 9,
22
- "2b": 8,
23
- "2c": 8,
24
- "1o": 7,
25
- "1c": 7,
26
- re: 6,
27
- ro: 6,
28
- rb: 6,
29
- rc: 6,
30
- ce: 5,
31
- co: 5,
32
- cb: 5,
33
- cc: 5,
34
- pe: 4,
35
- po: 4,
36
- pb: 4,
37
- pc: 4,
38
- "7b": 3,
39
- "7c": 3,
40
- "6e": 2,
41
- "6o": 2,
42
- "6b": 2,
43
- "6c": 2,
44
- "5e": 1,
45
- "5o": 1,
46
- "5b": 1,
47
- "5c": 1,
48
- "4e": 0,
49
- "4o": 0,
50
- "4b": 0,
51
- "4c": 0,
52
- }
53
-
54
- export const COLORS = [
55
- "#9b111",
56
- "#17c6c6",
57
- "#8c1d1d",
58
- "#9f9b9b",
59
- "#a5a5a5",
60
- "#f5a623",
61
- "#f44336",
62
- "#c2185b",
63
- ]
64
-
65
- export const TEAM_SIZE_VALUES = [1, 2, 3]
66
-
67
- export enum GAME_ERROR {
68
- UNEXPECTED_TEAM_SIZE = 'UNEXPECTED_TEAM_SIZE',
69
- TEAM_NOT_READY = 'TEAM_NOT_READY',
70
- TEAM_IS_FULL = 'TEAM_IS_FULL',
71
- }
72
-
73
- export const EnvidoCalculator: IEnvidoCalculator = {
74
- [EEnvidoCommand.ENVIDO]: () => ({
75
- accept: 2,
76
- decline: 1,
77
- next: [EEnvidoCommand.ENVIDO_ENVIDO, EEnvidoCommand.REAL_ENVIDO, EEnvidoCommand.FALTA_ENVIDO],
78
- }),
79
- [EEnvidoCommand.ENVIDO_ENVIDO]: () => ({
80
- accept: 4,
81
- decline: 2,
82
- next: [EEnvidoCommand.REAL_ENVIDO, EEnvidoCommand.FALTA_ENVIDO],
83
- }),
84
- [EEnvidoCommand.REAL_ENVIDO]: () => ({
85
- accept: 3,
86
- decline: 1,
87
- next: [EEnvidoCommand.FALTA_ENVIDO],
88
- }),
89
- [EEnvidoCommand.FALTA_ENVIDO]: (args) => {
90
- if (!args || !args.teams || !args.matchPoint) {
91
- return {
92
- accept: 1,
93
- decline: 1,
94
- next: [],
95
- }
96
- }
97
- const { teams, matchPoint } = args
98
- const totals = teams.map((team) => team.points.malas + team.points.buenas)
99
- const higher = getMaxNumberIndex(totals)
100
- const points = teams[higher].points
101
- const accept = points.buenas > 0 ? matchPoint - points.buenas : matchPoint - points.malas
102
- return {
103
- accept,
104
- decline: 2,
105
- next: [],
106
- }
107
- },
108
- }
package/src/lib/index.ts DELETED
@@ -1,135 +0,0 @@
1
- import { Match } from "./classes/Match"
2
- import { Player } from "./classes/Player"
3
- import { Table } from "./classes/Table"
4
- import { Team } from "./classes/Team"
5
- import { GAME_ERROR, TEAM_SIZE_VALUES } from "./constants"
6
- import { EHandState, IMatch, IPlayInstance, IPrivateTrucoshi, ITeam, ITrucoshi } from "./types"
7
-
8
- export type IWinnerCallback = (winner: ITeam, teams: [ITeam, ITeam]) => Promise<void>
9
- export type ITurnCallback = (play: IPlayInstance) => Promise<void>
10
- export type ITrucoCallback = (play: IPlayInstance) => Promise<void>
11
-
12
- export interface IGameLoop {
13
- _onTruco: ITrucoCallback
14
- _onTurn: ITurnCallback
15
- _onWinner: IWinnerCallback
16
- onTurn: (callback: ITurnCallback) => IGameLoop
17
- onWinner: (callback: IWinnerCallback) => IGameLoop
18
- onTruco: (callback: ITrucoCallback) => IGameLoop
19
- begin: () => void
20
- }
21
-
22
- const GameLoop = (match: IMatch) => {
23
- let gameloop: IGameLoop = {
24
- _onTruco: () => Promise.resolve(),
25
- _onTurn: () => Promise.resolve(),
26
- _onWinner: () => Promise.resolve(),
27
- onTruco: (callback: ITrucoCallback) => {
28
- gameloop._onTruco = callback
29
- return gameloop
30
- },
31
- onTurn: (callback: ITurnCallback) => {
32
- gameloop._onTurn = callback
33
- return gameloop
34
- },
35
- onWinner: (callback: IWinnerCallback) => {
36
- gameloop._onWinner = callback
37
- return gameloop
38
- },
39
- async begin() {
40
- while (!match.winner) {
41
- const play = match.play()
42
-
43
- if (!play || !play.player) {
44
- continue
45
- }
46
-
47
- if (play.state === EHandState.WAITING_FOR_TRUCO_ANSWER) {
48
- await gameloop._onTruco(play)
49
- continue
50
- }
51
-
52
- if (play.state === EHandState.WAITING_PLAY) {
53
- await gameloop._onTurn(play)
54
- continue
55
- }
56
- }
57
-
58
- await gameloop._onWinner(match.winner, match.teams)
59
- },
60
- }
61
-
62
- return gameloop
63
- }
64
-
65
- export function Trucoshi(teamSize?: 1 | 2 | 3) {
66
- const trucoshi: IPrivateTrucoshi = {
67
- lastTeamIdx: 1,
68
- _players: new Map(),
69
- get players() {
70
- return Array.from(trucoshi._players.values())
71
- },
72
- teams: [],
73
- table: null,
74
- maxPlayers: teamSize ? teamSize * 2 : 6,
75
- full: false,
76
- ready: false,
77
- calculateReady() {
78
- trucoshi.ready = trucoshi.players.reduce((prev, curr) => prev && curr.ready, true)
79
- return trucoshi.ready
80
- },
81
- calculateFull() {
82
- trucoshi.full = trucoshi._players.size >= trucoshi.maxPlayers
83
- return trucoshi.full
84
- },
85
- addPlayer(id, teamIdx) {
86
- const maxSize = teamSize ? teamSize : 3
87
- if (trucoshi.full || trucoshi.players.filter((p) => p.teamIdx === teamIdx).length > maxSize) {
88
- throw new Error(GAME_ERROR.TEAM_IS_FULL)
89
- }
90
- const player = Player(id, teamIdx !== undefined ? teamIdx : Number(!trucoshi.lastTeamIdx))
91
- trucoshi.lastTeamIdx = Number(!trucoshi.lastTeamIdx) as 0 | 1
92
- trucoshi._players.set(id, player)
93
- trucoshi.calculateFull()
94
- trucoshi.calculateReady()
95
- return player
96
- },
97
- removePlayer(id) {
98
- trucoshi._players.delete(id)
99
- trucoshi.calculateFull()
100
- trucoshi.calculateReady()
101
- return trucoshi
102
- },
103
- startMatch(matchPoint = 9) {
104
- trucoshi.calculateReady()
105
- const teamSize = trucoshi._players.size / 2
106
-
107
- if (!TEAM_SIZE_VALUES.includes(teamSize)) {
108
- throw new Error(GAME_ERROR.UNEXPECTED_TEAM_SIZE)
109
- }
110
-
111
- if (!trucoshi.ready) {
112
- throw new Error(GAME_ERROR.TEAM_NOT_READY)
113
- }
114
-
115
- trucoshi.teams.push(Team(trucoshi.players.filter((p) => p.teamIdx === 0)))
116
- trucoshi.teams.push(Team(trucoshi.players.filter((p) => p.teamIdx === 1)))
117
-
118
- if (
119
- trucoshi.teams[0].players.length !== teamSize ||
120
- trucoshi.teams[1].players.length !== teamSize
121
- ) {
122
- throw new Error(GAME_ERROR.UNEXPECTED_TEAM_SIZE)
123
- }
124
-
125
- trucoshi.table = Table(trucoshi.players, trucoshi.teams)
126
- return GameLoop(Match(trucoshi.table, trucoshi.teams, matchPoint))
127
- },
128
- }
129
-
130
- return {
131
- addPlayer: trucoshi.addPlayer,
132
- removePlayer: trucoshi.removePlayer,
133
- startMatch: trucoshi.startMatch,
134
- }
135
- }
package/src/lib/types.ts DELETED
@@ -1,216 +0,0 @@
1
- import { IGameLoop } from "."
2
- import { CARDS } from "./constants"
3
-
4
- export type ICard = keyof typeof CARDS
5
-
6
- export interface IDeck {
7
- cards: Array<ICard>
8
- usedCards: Array<ICard>
9
- takeCard(): ICard
10
- shuffle(): IDeck
11
- }
12
-
13
- export interface IPlayedCard {
14
- player: IPlayer
15
- card: ICard
16
- }
17
-
18
- export interface IPlayer {
19
- teamIdx: number
20
- id: string
21
- hand: Array<ICard>
22
- commands: Array<ECommand>
23
- usedHand: Array<ICard>
24
- disabled: boolean
25
- ready: boolean
26
- enable(): void
27
- disable(): void
28
- setReady(ready: boolean): void
29
- setHand(hand: Array<ICard>): Array<ICard>
30
- useCard(idx: number): ICard | null
31
- }
32
-
33
- export interface ITeam {
34
- _players: Map<string, IPlayer>
35
- players: Array<IPlayer>
36
- points: TeamPoints
37
- isTeamDisabled(): boolean
38
- disable(player: IPlayer): boolean
39
- addPoints(matchPoint: number, points: number): TeamPoints
40
- }
41
-
42
- export interface IMatch {
43
- teams: [ITeam, ITeam]
44
- hands: Array<IHand>
45
- winner: ITeam | null
46
- currentHand: IHand | null
47
- table: ITable
48
- play(): IPlayInstance | null
49
- addPoints(points: HandPoints): [ITeam, ITeam]
50
- pushHand(hand: IHand): void
51
- setCurrentHand(hand: IHand | null): IHand | null
52
- setWinner(winner: ITeam): void
53
- getNextTurn(): IteratorResult<IMatch | null, IMatch | null | void>
54
- }
55
-
56
- export interface TeamPoints {
57
- buenas: number
58
- malas: number
59
- winner: boolean
60
- }
61
-
62
- export interface HandPoints {
63
- 0: number
64
- 1: number
65
- }
66
-
67
- export interface RoundPoints {
68
- 0: number
69
- 1: number
70
- ties: number
71
- }
72
-
73
- export enum ESayCommand {
74
- QUIERO = "QUIERO",
75
- NO_QUIERO = "NO_QUIERO",
76
- TRUCO = "TRUCO",
77
- MAZO = "MAZO",
78
- FLOR = "FLOR",
79
- CONTRAFLOR = "CONTRAFLOR",
80
- }
81
-
82
- export enum EEnvidoCommand {
83
- ENVIDO = "ENVIDO",
84
- ENVIDO_ENVIDO = "ENVIDO_ENVIDO",
85
- REAL_ENVIDO = "REAL_ENVIDO",
86
- FALTA_ENVIDO = "FALTA_ENVIDO",
87
- }
88
-
89
- export type ECommand = ESayCommand | EEnvidoCommand
90
-
91
- export interface ITruco {
92
- state: 1 | 2 | 3 | 4
93
- teamIdx: 0 | 1 | null
94
- answer: boolean | null
95
- turn: number
96
- players: Array<IPlayer>
97
- currentPlayer: IPlayer | null
98
- generator: Generator<ITruco, void, unknown>
99
- sayTruco(teamIdx: 0 | 1, players: Array<IPlayer>): ITruco
100
- setPlayers(players: Array<IPlayer>): void
101
- setAnswer(answer: boolean | null): ITruco
102
- setTurn(turn: number): number
103
- setTeam(idx: 0 | 1): 0 | 1
104
- setCurrentPlayer(player: IPlayer | null): IPlayer | null
105
- getNextPlayer(): IteratorResult<ITruco, ITruco | void>
106
- }
107
-
108
- export interface EnvidoState {
109
- accept: number
110
- decline: number
111
- teamIdx: 0 | 1 | null
112
- }
113
-
114
- export interface IPlayInstance {
115
- teams: [ITeam, ITeam]
116
- handIdx: number
117
- roundIdx: number
118
- state: EHandState
119
- truco: ITruco
120
- envido: EnvidoState
121
- player: IPlayer | null
122
- commands: Array<ECommand> | null
123
- rounds: Array<IRound> | null
124
- use(idx: number): ICard | null
125
- say(command: ECommand): ECommand | null
126
- }
127
-
128
- export enum EHandState {
129
- WAITING_PLAY = "WAITING_PLAY",
130
- WAITING_FOR_TRUCO_ANSWER = "WAITING_FOR_TRUCO_ANSWER",
131
- WAITING_ENVIDO_ANSWER = "WAITING_ENVIDO_ANSWER",
132
- FINISHED = "FINISHED",
133
- }
134
-
135
- export type IHandCommands = {
136
- [key in ECommand]: (player: IPlayer) => void
137
- }
138
-
139
- export interface IHand {
140
- idx: number
141
- state: EHandState
142
- turn: number
143
- points: HandPoints
144
- truco: ITruco
145
- envido: EnvidoState
146
- rounds: Array<IRound>
147
- _currentPlayer: IPlayer | null
148
- get currentPlayer(): IPlayer | null
149
- set currentPlayer(player: IPlayer | null)
150
- currentRound: IRound | null
151
- commands: IHandCommands
152
- finished: () => boolean
153
- play(): IPlayInstance | null
154
- nextTurn(): void
155
- use(idx: number): ICard | null
156
- pushRound(round: IRound): IRound
157
- setTurn(turn: number): IPlayer
158
- addPoints(team: 0 | 1, points: number): void
159
- disablePlayer(player: IPlayer): void
160
- setCurrentRound(round: IRound | null): IRound | null
161
- setCurrentPlayer(player: IPlayer | null): IPlayer | null
162
- setState(state: EHandState): EHandState
163
- getNextPlayer(): IteratorResult<IHand, IHand | void>
164
- }
165
-
166
- export interface IPrivateTrucoshi {
167
- lastTeamIdx: 0 | 1
168
- _players: Map<string, IPlayer>
169
- get players(): Array<IPlayer>
170
- teams: Array<ITeam>
171
- maxPlayers: number
172
- table: ITable | null
173
- ready: boolean
174
- full: boolean
175
- addPlayer(id: string, teamIdx?: 0 | 1): IPlayer
176
- removePlayer(id: string): ITrucoshi
177
- calculateReady(): boolean
178
- calculateFull(): boolean
179
- startMatch(matchPoint?: 9 | 12 | 15): IGameLoop
180
- }
181
-
182
- export interface ITrucoshi extends Pick<IPrivateTrucoshi, 'addPlayer' | 'removePlayer' | 'startMatch'> {}
183
-
184
- export interface ITable {
185
- forehandIdx: number
186
- cards: Array<Array<IPlayedCard>>
187
- players: Array<IPlayer>
188
- nextTurn(): IPlayer
189
- player(idx?: number): IPlayer
190
- getPlayerPosition(id: string): number
191
- }
192
-
193
- export interface IRound {
194
- tie: boolean
195
- winner: IPlayer | null
196
- highest: number
197
- cards: Array<IPlayedCard>
198
- turn: number,
199
- nextTurn(): void
200
- use(playedCard: IPlayedCard): ICard
201
- }
202
-
203
- export type IEnvidoCalculatorResult = {
204
- accept: number
205
- decline: number
206
- next: Array<ECommand>
207
- }
208
-
209
- export type IEnvidoCalculatorArgs = {
210
- teams: [ITeam, ITeam]
211
- matchPoint: number
212
- }
213
-
214
- export type IEnvidoCalculator = {
215
- [key in EEnvidoCommand]: (args?: IEnvidoCalculatorArgs) => IEnvidoCalculatorResult
216
- }
package/src/lib/utils.ts DELETED
@@ -1,66 +0,0 @@
1
- import { CARDS } from "./constants"
2
- import { ICard, IRound, ITeam, RoundPoints } from "./types"
3
-
4
- export function getMaxNumberIndex<T = number>(array: Array<T>) {
5
- return array.reduce((accumulator, current, index) => {
6
- return current > array[accumulator] ? index : accumulator
7
- }, 0)
8
- }
9
-
10
- export function getCardValue(card: ICard) {
11
- return CARDS[card] || -1
12
- }
13
-
14
- export function shuffleArray<T = unknown>(array: Array<T>) {
15
- let currentIndex = array.length,
16
- randomIndex
17
-
18
- while (currentIndex != 0) {
19
- randomIndex = Math.floor(Math.random() * currentIndex)
20
- currentIndex--
21
- ;[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]
22
- }
23
-
24
- return array as Array<T>
25
- }
26
-
27
- export function checkHandWinner(
28
- rounds: Array<IRound>,
29
- forehandTeamIdx: 0 | 1
30
- ): null | 0 | 1 {
31
- const roundsWon: RoundPoints = {
32
- 0: 0,
33
- 1: 0,
34
- ties: 0,
35
- }
36
-
37
- for (let i = 0; i < rounds.length; i++) {
38
- const round = rounds[i]
39
- if (round.tie) {
40
- roundsWon[0] += 1
41
- roundsWon[1] += 1
42
- roundsWon.ties = roundsWon.ties + 1
43
- continue
44
- }
45
- if (round.winner?.teamIdx === 0) {
46
- roundsWon[0] += 1
47
- }
48
- if (round.winner?.teamIdx === 1) {
49
- roundsWon[1] += 1
50
- }
51
- }
52
-
53
- if ((roundsWon[0] > 2 && roundsWon[1] > 2) || (rounds.length > 2 && roundsWon.ties > 0)) {
54
- return forehandTeamIdx
55
- }
56
-
57
- if (roundsWon[0] >= 2 && roundsWon[1] < 2) {
58
- return 0
59
- }
60
-
61
- if (roundsWon[1] >= 2 && roundsWon[0] < 2) {
62
- return 1
63
- }
64
-
65
- return null
66
- }
@@ -1,34 +0,0 @@
1
- import { createServer } from "http"
2
- import { Server } from "socket.io"
3
- import { EClientEvent, EServerEvent } from "./types"
4
-
5
- const PORT = 4001
6
-
7
- const httpServer = createServer()
8
- const io = new Server(httpServer, {
9
- cors: {
10
- origin: "http://localhost:3000",
11
- methods: ["GET", "POST"],
12
- },
13
- })
14
-
15
- const sessions = new Map<string, string>()
16
-
17
- io.on("connection", (socket) => {
18
- socket.on(EClientEvent.PING, (msg) => {
19
- io.emit(EServerEvent.PONG, msg)
20
- })
21
-
22
- socket.on(EClientEvent.CREATE_MATCH, (msg) => {
23
-
24
- })
25
-
26
- socket.on(EClientEvent.SET_PLAYER_ID, (msg) => {
27
- if (typeof msg === 'string' && msg.length < 32) {
28
- }
29
- })
30
- })
31
-
32
- httpServer.listen(PORT)
33
-
34
- console.log("Listening on port", PORT)
File without changes
@@ -1,12 +0,0 @@
1
- export enum EClientEvent {
2
- PING = "PING",
3
- CREATE_MATCH = "CREATE_MATCH",
4
- JOIN_MATCH = "JOIN_MATCH",
5
- START_MATCH = "START_MATCH",
6
- SET_PLAYER_ID = "SET_PLAYER_ID",
7
- }
8
-
9
- export enum EServerEvent {
10
- SET_SESSION_ID = "SET_SESSION_ID",
11
- PONG = "PONG",
12
- }
@@ -1,57 +0,0 @@
1
- import { Trucoshi } from "../lib"
2
- import { IRound } from "../lib/types"
3
- ;(async () => {
4
- const trucoshi = Trucoshi()
5
-
6
- trucoshi.addPlayer("lukini").setReady(true)
7
- trucoshi.addPlayer("denoph").setReady(true)
8
- trucoshi.addPlayer("guada").setReady(true)
9
- trucoshi.addPlayer("juli").setReady(true)
10
- trucoshi.addPlayer("day").setReady(true)
11
- trucoshi.addPlayer("fran").setReady(true)
12
-
13
- trucoshi
14
- .startMatch()
15
- .onTurn(async (play) => {
16
- if (!play.player) {
17
- return
18
- }
19
- const name = play.player?.id.toUpperCase()
20
- console.log(`=== Mano ${play.handIdx} === Ronda ${play.roundIdx} === Turno de ${name} ===`)
21
- play.teams.map((team, id) =>
22
- console.log(`=== Team ${id} = ${team.points.malas} malas ${team.points.buenas} buenas ===`)
23
- )
24
- console.log(
25
- play.rounds && play.rounds.length
26
- ? play.rounds.map((round: IRound) =>
27
- round.cards.length ? round.cards.map((c) => [c.player.id, c.card]) : ""
28
- )
29
- : ""
30
- )
31
-
32
- const randomIdx = Math.round(Math.random() * (play.player.hand.length - 1))
33
- const handString = JSON.stringify(play.player.hand)
34
- const card = play.use(randomIdx)
35
-
36
- console.log(`\n${handString}\nUsing ${card}`)
37
- console.log(
38
- play.rounds && play.rounds.length
39
- ? play.rounds.map((round: IRound) =>
40
- round.cards.length ? round.cards.map((c) => [c.player.id, c.card]) : ""
41
- )
42
- : ""
43
- )
44
- })
45
- .onWinner(async (winner, teams) => {
46
- console.log("\n")
47
- teams.map((t, i) =>
48
- console.log(
49
- `Equipo ${i}: ${t.players.map((p) => ` ${p.id}`)} === ${t.points.malas} malas ${
50
- t.points.buenas
51
- } buenas`
52
- )
53
- )
54
- console.log(`\nEquipo Ganador:${winner.players.map((p) => ` ${p.id}`)}`)
55
- })
56
- .begin()
57
- })()
package/src/test/play.ts DELETED
@@ -1,122 +0,0 @@
1
- import * as readline from "readline"
2
- import { Trucoshi } from "../lib"
3
- import { IPlayInstance, IRound, ITeam } from "../lib/types"
4
-
5
- const command = (
6
- title: string,
7
- onLine: (line: string, close: () => void) => Promise<void>
8
- ): (() => Promise<void>) => {
9
- const promise = () =>
10
- new Promise<void>((resolve) => {
11
- const rl = readline.createInterface(process.stdin, process.stdout)
12
- rl.setPrompt(title)
13
- rl.prompt()
14
- rl.on("line", async (line) => {
15
- try {
16
- await onLine(line, () => rl.close())
17
- rl.close()
18
- resolve()
19
- } catch (e) {
20
- rl.close()
21
- return (async () => {
22
- await promise()
23
- resolve()
24
- })()
25
- }
26
- })
27
- })
28
-
29
- return promise
30
- }
31
-
32
- const playCommand = (play: IPlayInstance) =>
33
- command(
34
- `${play.player?.id} elije una carta [${play.player?.hand.map(
35
- (_c, i) => i + 1
36
- )}]: ${JSON.stringify(play.player?.hand)}\n`,
37
- async (idx) => {
38
- const playedCard = play.use(Number(idx) - 1)
39
- if (!playedCard) {
40
- return Promise.reject()
41
- }
42
- const handString = JSON.stringify(play.player?.hand)
43
- console.log(`\n${handString}\nUsing ${playedCard}`)
44
- console.log(
45
- play.rounds && play.rounds.length
46
- ? play.rounds.map((round: IRound) =>
47
- round.cards.length ? round.cards.map((c) => [c.player.id, c.card]) : ""
48
- )
49
- : ""
50
- )
51
- return Promise.resolve()
52
- }
53
- )
54
-
55
- const sayCommand = (play: IPlayInstance, canPlay: boolean) =>
56
- command(
57
- `${play.player?.id} elije una accion [${canPlay ? "0," : ""}${play.commands?.map(
58
- (_c, i) => i + 1
59
- )}]: ${
60
- canPlay ? JSON.stringify(["CARTA", ...(play.commands || [])]) : JSON.stringify(play.commands)
61
- }\n`,
62
- async (idx, close) => {
63
- const selectedCommand = play.commands?.[Number(idx) - 1]
64
-
65
- if (selectedCommand) {
66
- play.say(selectedCommand)
67
- return Promise.resolve()
68
- }
69
-
70
- if (idx === "0" && canPlay) {
71
- close()
72
- await playCommand(play)()
73
- return Promise.resolve()
74
- }
75
-
76
- return Promise.reject()
77
- }
78
- )
79
-
80
- ;(async () => {
81
- const trucoshi = Trucoshi()
82
-
83
- trucoshi.addPlayer("lukini", 0).setReady(true)
84
- trucoshi.addPlayer("guada", 0).setReady(true)
85
- trucoshi.addPlayer("denoph", 1).setReady(true)
86
- trucoshi.addPlayer("juli", 1).setReady(true)
87
-
88
- trucoshi
89
- .startMatch()
90
- .onTruco(async (play) => {
91
- await sayCommand(play, false)()
92
- })
93
- .onTurn(async (play) => {
94
- const name = play.player?.id.toUpperCase()
95
- console.log(`=== Mano ${play.handIdx} === Ronda ${play.roundIdx} === Turno de ${name} ===`)
96
-
97
- play.teams.map((team, id) =>
98
- console.log(`=== Team ${id} = ${team.points.malas} malas ${team.points.buenas} buenas`)
99
- )
100
-
101
- console.log(
102
- play.rounds && play.rounds.length
103
- ? play.rounds.map((round: IRound) =>
104
- round.cards.length ? round.cards.map((c) => [c.player.id, c.card]) : ""
105
- )
106
- : ""
107
- )
108
-
109
- await sayCommand(play, true)()
110
- })
111
- .onWinner(async (winner: ITeam, teams: [ITeam, ITeam]) => {
112
- teams.map((t, i) =>
113
- console.log(
114
- `Equipo ${i}: ${t.players.map((p) => ` ${p.id}`)} === ${t.points.malas} malas ${
115
- t.points.buenas
116
- } buenas`
117
- )
118
- )
119
- console.log(`\nEquipo Ganador:${winner?.players.map((p) => ` ${p.id}`)}`)
120
- })
121
- .begin()
122
- })()
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es5",
4
- "module": "commonjs",
5
- "lib": ["es6"],
6
- "allowJs": true,
7
- "outDir": "build",
8
- "rootDir": "src",
9
- "strict": true,
10
- "noImplicitAny": true,
11
- "esModuleInterop": true,
12
- "moduleResolution": "node",
13
- "resolveJsonModule": true,
14
- "declaration": true
15
- },
16
- "include": [
17
- "src/**/*"
18
- ]
19
- }