Skip to content

Commit e0e312d

Browse files
committed
Add match settings in dev-server
Also: * Rename default -> isDefault in game setting (`default` being a JS reserved keyword it was annoying in some places) * Normalize the game settings in `parseGame`
1 parent 6a330fe commit e0e312d

File tree

11 files changed

+708
-259
lines changed

11 files changed

+708
-259
lines changed

games/game1-v2.3.0/game/src/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { expect, test } from "vitest";
22

33
import { MatchTester as _MatchTester, MatchTesterOptions } from "@lefun/game";
44

5-
import { autoMove, game, RollGame as G, RollGameState as GS } from ".";
5+
import { autoMove, G, game, GS } from ".";
66

77
class MatchTester extends _MatchTester<GS, G> {
88
constructor(options: Omit<MatchTesterOptions<GS, G>, "game" | "autoMove">) {

games/game1-v2.3.0/game/src/index.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UserId } from "@lefun/core";
1+
import { GamePlayerSettings, GameSettings, UserId } from "@lefun/core";
22
import {
33
AutoMove,
44
BoardMove,
@@ -21,9 +21,12 @@ export type Board = {
2121

2222
sum: number;
2323
lastSomeBoardMoveValue?: number;
24+
25+
matchSettings: Record<string, string>;
26+
matchPlayersSettings: Record<UserId, Record<string, string>>;
2427
};
2528

26-
export type RollGameState = GameState<Board>;
29+
export type GS = GameState<Board>;
2730

2831
type MoveWithArgPayload = { someArg: string };
2932
type BoardMoveWithArgPayload = { someArg: number };
@@ -47,7 +50,7 @@ const playerStats = [
4750
] as const satisfies GameStats;
4851

4952
type PM<Payload = null> = PlayerMove<
50-
RollGameState,
53+
GS,
5154
Payload,
5255
PMT,
5356
BMT,
@@ -71,7 +74,10 @@ const roll: PM = {
7174
logPlayerStat,
7275
endMatch,
7376
}) {
74-
const diceValue = random.d6();
77+
const diceValue =
78+
board.matchPlayersSettings[userId].dieNumFaces === "6"
79+
? random.d6()
80+
: random.dice(20);
7581
board.players[userId].diceValue = diceValue;
7682
board.players[userId].isRolling = false;
7783
board.sum += diceValue;
@@ -102,7 +108,7 @@ const roll: PM = {
102108
},
103109
};
104110

105-
type BM<P = null> = BoardMove<RollGameState, P, PMT, BMT>;
111+
type BM<P = null> = BoardMove<GS, P, PMT, BMT>;
106112

107113
const initMove: BM = {
108114
execute({ board, turns }) {
@@ -122,8 +128,41 @@ const someBoardMoveWithArgs: BM<BoardMoveWithArgPayload> = {
122128
},
123129
};
124130

131+
const gameSettings: GameSettings = [
132+
{
133+
key: "setting1",
134+
options: [{ value: "a" }, { value: "b" }],
135+
},
136+
{ key: "setting2", options: [{ value: "x" }, { value: "y" }] },
137+
];
138+
139+
const gamePlayerSettings: GamePlayerSettings = [
140+
{
141+
key: "color",
142+
type: "color",
143+
exclusive: true,
144+
options: [
145+
{ value: "red", label: "red" },
146+
{ value: "blue", label: "blue" },
147+
{ value: "green", label: "green" },
148+
{ value: "orange", label: "orange" },
149+
{ value: "pink", label: "pink" },
150+
{ value: "brown", label: "brown" },
151+
{ value: "black", label: "black" },
152+
{ value: "darkgreen", label: "darkgreen" },
153+
{ value: "darkred", label: "darkred" },
154+
{ value: "purple", label: "purple" },
155+
],
156+
},
157+
{
158+
key: "dieNumFaces",
159+
type: "string",
160+
options: [{ value: "6" }, { value: "20" }],
161+
},
162+
];
163+
125164
export const game = {
126-
initialBoards({ players }) {
165+
initialBoards({ players, matchSettings, matchPlayersSettings }) {
127166
return {
128167
board: {
129168
sum: 0,
@@ -132,6 +171,8 @@ export const game = {
132171
),
133172
playerOrder: [...players],
134173
currentPlayerIndex: 0,
174+
matchSettings,
175+
matchPlayersSettings,
135176
},
136177
};
137178
},
@@ -141,11 +182,13 @@ export const game = {
141182
maxPlayers: 10,
142183
matchStats,
143184
playerStats,
144-
} satisfies Game<RollGameState, PMT, BMT>;
185+
gameSettings,
186+
gamePlayerSettings,
187+
} satisfies Game<GS, PMT, BMT>;
145188

146-
export type RollGame = typeof game;
189+
export type G = typeof game;
147190

148-
export const autoMove: AutoMove<RollGameState, RollGame> = ({ random }) => {
191+
export const autoMove: AutoMove<GS, G> = ({ random }) => {
149192
if (random.d2() === 1) {
150193
return ["moveWithArg", { someArg: "123" }];
151194
}

games/game1-v2.3.0/ui/src/Board.tsx

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,23 @@ import {
1212
useUsername,
1313
} from "@lefun/ui";
1414

15-
import type { RollGame, RollGameState } from "game1-v2.3.0-game";
15+
import type { G, GS } from "game1-v2.3.0-game";
1616

17-
// Dice symbol characters
18-
const DICE = ["", "\u2680", "\u2681", "\u2682", "\u2683", "\u2684", "\u2685"];
19-
20-
const useSelector = makeUseSelector<RollGameState>();
21-
const useSelectorShallow = makeUseSelectorShallow<RollGameState>();
22-
const useMakeMove = makeUseMakeMove<RollGame>();
17+
const useSelector = makeUseSelector<GS>();
18+
const useSelectorShallow = makeUseSelectorShallow<GS>();
19+
const useMakeMove = makeUseMakeMove<G>();
2320

2421
function Player({ userId }: { userId: UserId }) {
2522
const itsMe = useSelector((state) => state.userId === userId);
2623
const username = useUsername(userId);
2724

25+
const color = useSelector(
26+
(state) => state.board.matchPlayersSettings[userId].color,
27+
);
28+
2829
return (
2930
<div className="player">
30-
<span className={classNames(itsMe && "bold")}>{username}</span>
31+
<span className={classNames(itsMe && "bold", color)}>{username}</span>
3132
<Die userId={userId} />
3233
</div>
3334
);
@@ -42,9 +43,10 @@ function Die({ userId }: { userId: UserId }) {
4243
);
4344

4445
return (
45-
<span className="dice">
46-
{isRolling || !diceValue ? "?" : DICE[diceValue]}
47-
</span>
46+
<div>
47+
Dice Value:{" "}
48+
<span className="dice">{isRolling || !diceValue ? "?" : diceValue}</span>
49+
</div>
4850
);
4951
}
5052

@@ -54,12 +56,19 @@ function Board() {
5456
Object.keys(state.board.players),
5557
);
5658

59+
const matchSettings = useSelector((state) => state.board.matchSettings);
60+
5761
const isPlayer = useIsPlayer();
5862

5963
return (
6064
<div>
6165
<div>
6266
<Trans>The template game</Trans>
67+
{Object.entries(matchSettings).map(([key, value]) => (
68+
<div key={key}>
69+
<span className="bold">{key}:</span> {value}
70+
</div>
71+
))}
6372
{players.map((userId) => (
6473
<Player key={userId} userId={userId} />
6574
))}

games/game1-v2.3.0/ui/src/index.css

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,53 @@ button {
2626
}
2727

2828
.dice {
29-
margin: 0 0 0 10px;
30-
font-size: 3rem;
29+
font-weight: bold;
30+
font-size: 1.2rem;
3131
}
3232

3333
.player {
34-
height: 100px;
34+
display: flex;
35+
flex-direction: column;
36+
justify-content: center;
37+
height: 80px;
38+
}
39+
40+
.red {
41+
color: red;
42+
}
43+
44+
.blue {
45+
color: blue;
46+
}
47+
48+
.green {
49+
color: green;
50+
}
51+
52+
.orange {
53+
color: orange;
54+
}
55+
56+
.pink {
57+
color: pink;
58+
}
59+
60+
.brown {
61+
color: brown;
62+
}
63+
64+
.black {
65+
color: black;
66+
}
67+
68+
.darkgreen {
69+
color: darkgreen;
70+
}
71+
72+
.darkred {
73+
color: darkred;
74+
}
75+
76+
.purple {
77+
color: purple;
3578
}

games/game1-v2.3.0/ui/src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ render({
1616
},
1717
game,
1818
messages: { en, fr },
19-
gameId: "roll",
19+
gameId: "game1-v2.3.0",
2020
});

packages/core/src/types.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const LOCALES: Locale[] = ["fr", "en"];
1616
export type GameSettingOption = {
1717
value: string;
1818
// Is it the default option. If it's missing we'll take the first one.
19-
default?: boolean;
19+
isDefault?: boolean;
2020

2121
// Keeping as optional for backward compatibility.
2222
label?: string;
@@ -51,14 +51,19 @@ export type GameSetting = {
5151

5252
export type GameSettings = GameSetting[];
5353

54+
export type GameSettings_ = {
55+
allIds: string[];
56+
byId: Record<string, GameSetting>;
57+
};
58+
5459
/*
5560
* Fields common to all the player setting options.
5661
*/
5762
export type CommonPlayerSettingOption = {
5863
value: string;
5964
// Is it the default option? If none is the default, we will fallback on the first
6065
// player option as the default.
61-
default?: boolean;
66+
isDefault?: boolean;
6267
};
6368

6469
type ColorPlayerSettingOption = {
@@ -75,6 +80,7 @@ type StringPlayerSettingOption = {
7580
// Note that some fields are common to all types of game player setting, and some
7681
// depend on the type.
7782
export type GamePlayerSetting = {
83+
key: string;
7884
// Can different players have the same selected option?
7985
// By default we assume *not* exclusive.
8086
exclusive?: boolean;
@@ -94,15 +100,12 @@ export type GamePlayerSetting = {
94100
| { type: "string"; options: StringPlayerSettingOption[] }
95101
);
96102

97-
// FIXME This should be a list, to get an order, like the game options. This will be a problem when we have
98-
// more than one option (which is not the case yet!).
99-
// But at the same time being able to query the option using a string is useful, and
100-
// it's missing in the Game Options. Ideally find a way to have the best of both worlds,
101-
// for both the (regular) options and the player options... without making it to
102-
// cumbersome for the game developer! We probably want to internally build a allIds/byId
103-
// scheme from the list of options, and split the "game player options DEF" with the
104-
// "gamePlayerSettings" that we store.
105-
export type GamePlayerSettings = Record<string, GamePlayerSetting>;
103+
export type GamePlayerSettings = GamePlayerSetting[];
104+
105+
export type GamePlayerSettings_ = {
106+
allIds: string[];
107+
byId: Record<string, GamePlayerSetting>;
108+
};
106109

107110
export type GameStatType =
108111
| "integer"

0 commit comments

Comments
 (0)