trucoshi 0.0.4 → 0.0.5
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 +3 -2
- package/build/lib/classes/Hand.js +87 -29
- package/build/lib/classes/Match.d.ts +2 -2
- package/build/lib/classes/Match.js +2 -3
- package/build/lib/classes/Play.js +6 -12
- package/build/lib/classes/Player.js +12 -0
- package/build/lib/classes/Round.d.ts +1 -1
- package/build/lib/classes/Round.js +5 -1
- package/build/lib/classes/Table.d.ts +2 -2
- package/build/lib/classes/Table.js +3 -10
- package/build/lib/classes/Team.js +8 -0
- package/build/lib/classes/Truco.d.ts +2 -0
- package/build/lib/classes/Truco.js +110 -0
- package/build/lib/constants.d.ts +6 -0
- package/build/lib/constants.js +8 -1
- package/build/lib/index.d.ts +13 -6
- package/build/lib/index.js +83 -10
- package/build/lib/types.d.ts +50 -5
- package/build/lib/types.js +2 -0
- package/build/lib/utils.d.ts +2 -2
- package/build/lib/utils.js +1 -18
- package/build/server/match.d.ts +0 -0
- package/build/server/match.js +1 -0
- package/build/test/autoplay.js +11 -2
- package/build/test/play.js +70 -51
- package/package.json +1 -1
- package/src/lib/classes/Hand.ts +82 -26
- package/src/lib/classes/Match.ts +3 -4
- package/src/lib/classes/Play.ts +9 -17
- package/src/lib/classes/Player.ts +13 -1
- package/src/lib/classes/Round.ts +5 -1
- package/src/lib/classes/Table.ts +8 -13
- package/src/lib/classes/Team.ts +7 -0
- package/src/lib/classes/Truco.ts +72 -0
- package/src/lib/constants.ts +8 -0
- package/src/lib/index.ts +95 -17
- package/src/lib/types.ts +51 -5
- package/src/lib/utils.ts +1 -22
- package/src/server/match.ts +0 -0
- package/src/test/autoplay.ts +12 -2
- package/src/test/play.ts +64 -55
package/src/lib/classes/Hand.ts
CHANGED
|
@@ -6,17 +6,18 @@ import {
|
|
|
6
6
|
IHand,
|
|
7
7
|
IHandCommands,
|
|
8
8
|
IMatch,
|
|
9
|
-
IPlayer,
|
|
10
9
|
} from "../types"
|
|
11
10
|
import { checkHandWinner } from "../utils"
|
|
12
11
|
import { PlayInstance } from "./Play"
|
|
13
12
|
import { Round } from "./Round"
|
|
13
|
+
import { Truco } from "./Truco"
|
|
14
14
|
|
|
15
15
|
export function Hand(match: IMatch, deck: IDeck, idx: number) {
|
|
16
16
|
match.teams.forEach((team) => {
|
|
17
17
|
team.players.forEach((player) => {
|
|
18
18
|
const playerHand = [deck.takeCard(), deck.takeCard(), deck.takeCard()]
|
|
19
19
|
player.setHand(playerHand)
|
|
20
|
+
player.enable()
|
|
20
21
|
// player.setHand(["5c", "4c", "6c"])
|
|
21
22
|
})
|
|
22
23
|
})
|
|
@@ -26,9 +27,7 @@ export function Hand(match: IMatch, deck: IDeck, idx: number) {
|
|
|
26
27
|
let forehandTeamIdx = match.table.player(hand.turn).teamIdx as 0 | 1
|
|
27
28
|
|
|
28
29
|
while (currentRoundIdx < 3 && !hand.finished()) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const round = Round()
|
|
30
|
+
const round = Round(0)
|
|
32
31
|
hand.setCurrentRound(round)
|
|
33
32
|
hand.pushRound(round)
|
|
34
33
|
|
|
@@ -42,30 +41,45 @@ export function Hand(match: IMatch, deck: IDeck, idx: number) {
|
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
while (
|
|
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
|
+
|
|
46
55
|
const player = match.table.player(hand.turn)
|
|
47
56
|
hand.setCurrentPlayer(player)
|
|
48
|
-
if (
|
|
57
|
+
if (player.disabled) {
|
|
49
58
|
hand.setCurrentPlayer(null)
|
|
50
59
|
}
|
|
51
60
|
|
|
52
|
-
if (hand.turn >= match.table.players.length - 1) {
|
|
53
|
-
hand.setTurn(0)
|
|
54
|
-
} else {
|
|
55
|
-
hand.setTurn(hand.turn + 1)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
i++
|
|
59
|
-
|
|
60
61
|
yield hand
|
|
61
62
|
}
|
|
62
63
|
|
|
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
|
+
}
|
|
64
77
|
|
|
65
|
-
if (
|
|
66
|
-
hand.addPoints(
|
|
78
|
+
if (winnerTeamIdx !== null) {
|
|
79
|
+
hand.addPoints(winnerTeamIdx, hand.truco.state)
|
|
67
80
|
hand.setState(EHandState.FINISHED)
|
|
68
81
|
}
|
|
82
|
+
|
|
69
83
|
currentRoundIdx++
|
|
70
84
|
}
|
|
71
85
|
yield hand
|
|
@@ -81,6 +95,19 @@ export function Hand(match: IMatch, deck: IDeck, idx: number) {
|
|
|
81
95
|
const { teamIdx } = hand.truco
|
|
82
96
|
if (teamIdx === null || teamIdx !== player.teamIdx) {
|
|
83
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)
|
|
84
111
|
}
|
|
85
112
|
},
|
|
86
113
|
[ESayCommand.FLOR]: () => {},
|
|
@@ -96,28 +123,57 @@ export function Hand(match: IMatch, deck: IDeck, idx: number) {
|
|
|
96
123
|
turn: Number(match.table.forehandIdx),
|
|
97
124
|
state: EHandState.WAITING_PLAY,
|
|
98
125
|
rounds: [],
|
|
99
|
-
truco:
|
|
100
|
-
state: 1,
|
|
101
|
-
teamIdx: null,
|
|
102
|
-
},
|
|
126
|
+
truco: Truco(),
|
|
103
127
|
envido: {
|
|
104
128
|
accept: 1,
|
|
105
129
|
decline: 2,
|
|
106
130
|
teamIdx: null,
|
|
107
131
|
},
|
|
108
132
|
points: [0, 0],
|
|
109
|
-
disabledPlayerIds: [],
|
|
110
133
|
currentRound: null,
|
|
111
|
-
|
|
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
|
+
},
|
|
112
144
|
commands,
|
|
113
145
|
play() {
|
|
114
146
|
return PlayInstance(hand, match.teams)
|
|
115
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
|
+
},
|
|
116
172
|
getNextPlayer() {
|
|
117
173
|
return roundsGenerator.next()
|
|
118
174
|
},
|
|
119
175
|
disablePlayer(player) {
|
|
120
|
-
|
|
176
|
+
match.teams[player.teamIdx].disable(player)
|
|
121
177
|
},
|
|
122
178
|
addPoints(team, points) {
|
|
123
179
|
hand.points[team] = hand.points[team] + points
|
|
@@ -135,8 +191,8 @@ export function Hand(match: IMatch, deck: IDeck, idx: number) {
|
|
|
135
191
|
return hand.currentRound
|
|
136
192
|
},
|
|
137
193
|
setCurrentPlayer(player) {
|
|
138
|
-
hand.
|
|
139
|
-
return hand.
|
|
194
|
+
hand._currentPlayer = player
|
|
195
|
+
return hand._currentPlayer
|
|
140
196
|
},
|
|
141
197
|
setState(state) {
|
|
142
198
|
hand.state = state
|
package/src/lib/classes/Match.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { IHand, IMatch, ITeam } from "../types"
|
|
1
|
+
import { IHand, IMatch, ITable, ITeam } from "../types"
|
|
2
2
|
import { Deck } from "./Deck"
|
|
3
3
|
import { Hand } from "./Hand"
|
|
4
|
-
import { Table } from "./Table"
|
|
5
4
|
|
|
6
|
-
export function Match(teams: Array<ITeam> = [], matchPoint: number = 9): IMatch {
|
|
5
|
+
export function Match(table: ITable, teams: Array<ITeam> = [], matchPoint: number = 9): IMatch {
|
|
7
6
|
const deck = Deck().shuffle()
|
|
8
7
|
|
|
9
8
|
const size = teams[0].players.length
|
|
@@ -47,7 +46,7 @@ export function Match(teams: Array<ITeam> = [], matchPoint: number = 9): IMatch
|
|
|
47
46
|
winner: null,
|
|
48
47
|
teams: teams as [ITeam, ITeam],
|
|
49
48
|
hands: [],
|
|
50
|
-
table
|
|
49
|
+
table,
|
|
51
50
|
currentHand: null,
|
|
52
51
|
play() {
|
|
53
52
|
match.getNextTurn()
|
package/src/lib/classes/Play.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { EEnvidoCommand, ESayCommand, IHand, IPlayInstance, ITeam } from "../types"
|
|
1
|
+
import { EEnvidoCommand, EHandState, ESayCommand, IHand, IPlayInstance, ITeam } from "../types"
|
|
2
2
|
|
|
3
3
|
export function PlayInstance(hand: IHand, teams: [ITeam, ITeam]) {
|
|
4
|
-
|
|
5
4
|
const instance: IPlayInstance = {
|
|
6
5
|
state: hand.state,
|
|
7
6
|
teams,
|
|
@@ -13,30 +12,19 @@ export function PlayInstance(hand: IHand, teams: [ITeam, ITeam]) {
|
|
|
13
12
|
commands: [],
|
|
14
13
|
rounds: hand.rounds,
|
|
15
14
|
use(idx) {
|
|
16
|
-
|
|
17
|
-
const round = hand.currentRound
|
|
18
|
-
if (!player || !round) {
|
|
19
|
-
return null
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const card = player.useCard(idx)
|
|
23
|
-
if (card) {
|
|
24
|
-
return round.use({ player, card })
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return null
|
|
15
|
+
return hand.use(idx)
|
|
28
16
|
},
|
|
29
17
|
say(command) {
|
|
30
|
-
if (!hand.
|
|
18
|
+
if (!hand._currentPlayer || !instance.commands?.includes(command)) {
|
|
31
19
|
return null
|
|
32
20
|
}
|
|
33
21
|
|
|
34
|
-
hand.commands[command](hand.
|
|
22
|
+
hand.commands[command](hand._currentPlayer)
|
|
35
23
|
|
|
36
24
|
return command
|
|
37
25
|
},
|
|
38
26
|
}
|
|
39
|
-
|
|
27
|
+
|
|
40
28
|
instance.commands?.push(ESayCommand.MAZO)
|
|
41
29
|
instance.commands?.push(ESayCommand.TRUCO)
|
|
42
30
|
|
|
@@ -44,5 +32,9 @@ export function PlayInstance(hand: IHand, teams: [ITeam, ITeam]) {
|
|
|
44
32
|
instance.commands?.push(EEnvidoCommand.ENVIDO)
|
|
45
33
|
}
|
|
46
34
|
|
|
35
|
+
if (hand.state === EHandState.WAITING_FOR_TRUCO_ANSWER) {
|
|
36
|
+
instance.commands = [ESayCommand.TRUCO, ESayCommand.QUIERO, ESayCommand.NO_QUIERO]
|
|
37
|
+
}
|
|
38
|
+
|
|
47
39
|
return instance
|
|
48
40
|
}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IPlayer } from "../types"
|
|
2
2
|
|
|
3
3
|
export function Player(id: string, teamIdx: number) {
|
|
4
4
|
const player: IPlayer = {
|
|
5
5
|
id,
|
|
6
6
|
teamIdx,
|
|
7
7
|
hand: [],
|
|
8
|
+
commands: [],
|
|
8
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
|
+
},
|
|
9
21
|
setHand(hand) {
|
|
10
22
|
player.hand = hand
|
|
11
23
|
player.usedHand = []
|
package/src/lib/classes/Round.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { IRound } from "../types"
|
|
2
2
|
import { getCardValue } from "../utils"
|
|
3
3
|
|
|
4
|
-
export function Round(): IRound {
|
|
4
|
+
export function Round(turn: number): IRound {
|
|
5
5
|
const round: IRound = {
|
|
6
|
+
turn,
|
|
6
7
|
highest: -1,
|
|
7
8
|
winner: null,
|
|
8
9
|
cards: [],
|
|
9
10
|
tie: false,
|
|
11
|
+
nextTurn() {
|
|
12
|
+
round.turn++
|
|
13
|
+
},
|
|
10
14
|
use({ card, player }) {
|
|
11
15
|
const value = getCardValue(card)
|
|
12
16
|
if (value === round.highest && player.teamIdx !== round.winner?.teamIdx) {
|
package/src/lib/classes/Table.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {
|
|
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"
|
|
2
6
|
|
|
3
|
-
export function Table(
|
|
7
|
+
export function Table(players: Array<IPlayer>, teams: Array<ITeam>): ITable {
|
|
4
8
|
const table: ITable = {
|
|
5
|
-
players
|
|
9
|
+
players,
|
|
6
10
|
cards: [],
|
|
7
11
|
forehandIdx: 0,
|
|
8
12
|
nextTurn() {
|
|
9
|
-
if (table.forehandIdx <
|
|
13
|
+
if (table.forehandIdx < table.players.length - 1) {
|
|
10
14
|
table.forehandIdx++
|
|
11
15
|
} else {
|
|
12
16
|
table.forehandIdx = 0
|
|
@@ -24,14 +28,5 @@ export function Table(teams: Array<ITeam>, size: number): ITable {
|
|
|
24
28
|
},
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
if (teams[0].players.length != size || teams[1].players.length != size) {
|
|
28
|
-
throw new Error("Unexpected team size")
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
for (let i = 0; i < size; i++) {
|
|
32
|
-
table.players.push(teams[0].players[i])
|
|
33
|
-
table.players.push(teams[1].players[i])
|
|
34
|
-
}
|
|
35
|
-
|
|
36
31
|
return table
|
|
37
32
|
}
|
package/src/lib/classes/Team.ts
CHANGED
|
@@ -11,6 +11,13 @@ export function Team(players: Array<IPlayer>) {
|
|
|
11
11
|
malas: 0,
|
|
12
12
|
winner: false,
|
|
13
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
|
+
},
|
|
14
21
|
addPoints(matchPoint, points) {
|
|
15
22
|
const malas = team.points.malas + points
|
|
16
23
|
const diff = malas - matchPoint
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
}
|
package/src/lib/constants.ts
CHANGED
|
@@ -62,6 +62,14 @@ export const COLORS = [
|
|
|
62
62
|
"#c2185b",
|
|
63
63
|
]
|
|
64
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
|
+
|
|
65
73
|
export const EnvidoCalculator: IEnvidoCalculator = {
|
|
66
74
|
[EEnvidoCommand.ENVIDO]: () => ({
|
|
67
75
|
accept: 2,
|
package/src/lib/index.ts
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import { Match } from "./classes/Match"
|
|
2
2
|
import { Player } from "./classes/Player"
|
|
3
|
+
import { Table } from "./classes/Table"
|
|
3
4
|
import { Team } from "./classes/Team"
|
|
4
|
-
import {
|
|
5
|
+
import { GAME_ERROR, TEAM_SIZE_VALUES } from "./constants"
|
|
6
|
+
import { EHandState, IMatch, IPlayInstance, IPrivateTrucoshi, ITeam, ITrucoshi } from "./types"
|
|
5
7
|
|
|
6
8
|
export type IWinnerCallback = (winner: ITeam, teams: [ITeam, ITeam]) => Promise<void>
|
|
7
|
-
export type ITurnCallback = (
|
|
9
|
+
export type ITurnCallback = (play: IPlayInstance) => Promise<void>
|
|
10
|
+
export type ITrucoCallback = (play: IPlayInstance) => Promise<void>
|
|
8
11
|
|
|
9
12
|
export interface IGameLoop {
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
_onTruco: ITrucoCallback
|
|
14
|
+
_onTurn: ITurnCallback
|
|
15
|
+
_onWinner: IWinnerCallback
|
|
12
16
|
onTurn: (callback: ITurnCallback) => IGameLoop
|
|
13
17
|
onWinner: (callback: IWinnerCallback) => IGameLoop
|
|
14
|
-
|
|
18
|
+
onTruco: (callback: ITrucoCallback) => IGameLoop
|
|
19
|
+
begin: () => void
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
const GameLoop = (match: IMatch) => {
|
|
18
23
|
let gameloop: IGameLoop = {
|
|
24
|
+
_onTruco: () => Promise.resolve(),
|
|
19
25
|
_onTurn: () => Promise.resolve(),
|
|
20
26
|
_onWinner: () => Promise.resolve(),
|
|
27
|
+
onTruco: (callback: ITrucoCallback) => {
|
|
28
|
+
gameloop._onTruco = callback
|
|
29
|
+
return gameloop
|
|
30
|
+
},
|
|
21
31
|
onTurn: (callback: ITurnCallback) => {
|
|
22
32
|
gameloop._onTurn = callback
|
|
23
33
|
return gameloop
|
|
@@ -26,7 +36,7 @@ const GameLoop = (match: IMatch) => {
|
|
|
26
36
|
gameloop._onWinner = callback
|
|
27
37
|
return gameloop
|
|
28
38
|
},
|
|
29
|
-
async
|
|
39
|
+
async begin() {
|
|
30
40
|
while (!match.winner) {
|
|
31
41
|
const play = match.play()
|
|
32
42
|
|
|
@@ -34,7 +44,15 @@ const GameLoop = (match: IMatch) => {
|
|
|
34
44
|
continue
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
|
|
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
|
+
}
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
await gameloop._onWinner(match.winner, match.teams)
|
|
@@ -44,14 +62,74 @@ const GameLoop = (match: IMatch) => {
|
|
|
44
62
|
return gameloop
|
|
45
63
|
}
|
|
46
64
|
|
|
47
|
-
export function Trucoshi(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
+
}
|
|
57
135
|
}
|