code-battles 1.6.4 → 1.7.0

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/dist/cjs/index.js CHANGED
@@ -374,8 +374,8 @@ const updatePointModifier = () => {
374
374
  const pointModifier = {};
375
375
  for (const round of rounds) {
376
376
  if (results[round.players.join(", ")] &&
377
- results[round.players.join(", ")][round.map]) {
378
- for (const result of results[round.players.join(", ")][round.map]) {
377
+ results[round.players.join(", ")][JSON.stringify(round.parameters)]) {
378
+ for (const result of results[round.players.join(", ")][JSON.stringify(round.parameters)]) {
379
379
  const first = round.players[result[0]];
380
380
  const second = round.players[result[1]];
381
381
  if (!pointModifier[first]) {
@@ -403,11 +403,11 @@ const toPlacing = (n) => {
403
403
  }
404
404
  return n.toString() + DIGITS[n % 10];
405
405
  };
406
- const runNoUI = (map, apis, playerBots, seed, verbose) => {
406
+ const runNoUI = (parameters, apis, playerBots, seed, verbose) => {
407
407
  const players = playerBots.map((api) => (api === "None" ? "" : apis[api]));
408
408
  tryUntilSuccess(() =>
409
409
  // @ts-ignore
410
- window._startSimulation(map, players, playerBots, true, false, verbose, seed));
410
+ window._startSimulation(parameters, players, playerBots, true, false, verbose, seed));
411
411
  };
412
412
  const tryUntilSuccess = (f, timeout = 500) => {
413
413
  try {
@@ -2466,7 +2466,7 @@ const PickBotBlock = () => {
2466
2466
 
2467
2467
  const BotSelector = ({ playerCount, setPlayerCount, playerBots, setPlayerBots, apis, }) => {
2468
2468
  return (React.createElement(React.Fragment, null,
2469
- React.createElement(core.NumberInput, { mt: "xs", label: "Player Count", value: playerCount, min: 1, onChange: (c) => {
2469
+ React.createElement(core.NumberInput, { label: "Player Count", value: playerCount, min: 1, onChange: (c) => {
2470
2470
  if (typeof c === "number") {
2471
2471
  setPlayerCount(c);
2472
2472
  while (playerBots.length < c) {
@@ -2489,9 +2489,9 @@ const BotSelector = ({ playerCount, setPlayerCount, playerBots, setPlayerBots, a
2489
2489
  const RunSimulationBlock = () => {
2490
2490
  const [apis, loading] = useAPIs();
2491
2491
  const configuration = useConfiguration();
2492
- const [map, setMap] = useLocalStorage({
2493
- key: "Map",
2494
- defaultValue: configuration.maps[0],
2492
+ const [parameters, setParameters] = useLocalStorage({
2493
+ key: "Parameters",
2494
+ defaultValue: {},
2495
2495
  });
2496
2496
  const [playerCount, setPlayerCount] = useLocalStorage({
2497
2497
  key: "Player Count",
@@ -2512,19 +2512,30 @@ const RunSimulationBlock = () => {
2512
2512
  if (Object.keys(runningNoUIN).length === 1) {
2513
2513
  remaining = Math.max(...Object.values(runningNoUIN));
2514
2514
  }
2515
+ const getFullParameters = () => {
2516
+ var _a;
2517
+ const result = {};
2518
+ for (const key in configuration.parameters) {
2519
+ result[key] = (_a = parameters[key]) !== null && _a !== void 0 ? _a : configuration.parameters[key][0];
2520
+ }
2521
+ return result;
2522
+ };
2515
2523
  const run = () => {
2516
2524
  // @ts-ignore
2517
2525
  window._isSimulationFromFile = false;
2518
- navigate(`/simulation/${map.replaceAll(" ", "-")}/${playerBots.map(encodeURIComponent).join(",")}?seed=${seed === "-" ? "" : seed}`);
2526
+ const params = getFullParameters();
2527
+ navigate(`/simulation/${playerBots.map(encodeURIComponent).join(",")}?seed=${seed === "-" ? "" : seed}&${Object.keys(params)
2528
+ .map((p) => `${p}=${encodeURIComponent(params[p])}`)
2529
+ .join("&")}`);
2519
2530
  };
2520
2531
  const startRunNoUI = () => {
2521
2532
  setRunningNoUI(true);
2522
- runNoUI(map, apis, playerBots, seed.toString(), false);
2533
+ runNoUI(getFullParameters(), apis, playerBots, seed.toString(), false);
2523
2534
  };
2524
2535
  const startRunNoUIN = (n) => {
2525
2536
  setLocalStorage("Results", {});
2526
2537
  setRunningNoUIN({ [n.toString()]: n });
2527
- runNoUI(map, apis, playerBots, seed.toString(), false);
2538
+ runNoUI(getFullParameters(), apis, playerBots, seed.toString(), false);
2528
2539
  };
2529
2540
  React.useEffect(() => {
2530
2541
  // @ts-ignore
@@ -2553,7 +2564,7 @@ const RunSimulationBlock = () => {
2553
2564
  for (const key in runningNoUIN) {
2554
2565
  if (runningNoUIN[key] == 0) {
2555
2566
  const results = getLocalStorage("Results");
2556
- const currentResults = results[playerBots.join(", ")][map];
2567
+ const currentResults = results[playerBots.join(", ")][JSON.stringify(getFullParameters())];
2557
2568
  const winCounts = {};
2558
2569
  for (const result of currentResults) {
2559
2570
  const winner = playerBots[result[0]];
@@ -2578,16 +2589,18 @@ const RunSimulationBlock = () => {
2578
2589
  setLocalStorage("Results", {});
2579
2590
  }
2580
2591
  else {
2581
- runNoUI(map, apis, playerBots, seed.toString(), false);
2592
+ runNoUI(getFullParameters(), apis, playerBots, seed.toString(), false);
2582
2593
  }
2583
2594
  }
2584
2595
  }, [runningNoUIN]);
2585
2596
  return (React.createElement(Block, { title: "Run Simulation", logo: "fa-solid fa-display" },
2586
- React.createElement(core.Select, { leftSection: React.createElement("i", { className: "fa-solid fa-earth-americas" }), label: "Map", data: configuration.maps, value: map, onChange: (s) => {
2587
- if (s) {
2588
- setMap(s);
2589
- }
2590
- } }),
2597
+ Object.keys(configuration.parameters)
2598
+ .sort()
2599
+ .map((parameter, parameterIndex) => {
2600
+ var _a, _b;
2601
+ const icon = ((_a = configuration.parameterIcons) !== null && _a !== void 0 ? _a : {})[parameter];
2602
+ return (React.createElement(core.Select, { mb: "xs", key: parameterIndex, leftSection: icon ? React.createElement("i", { className: icon }) : undefined, label: parameter[0].toUpperCase() + parameter.slice(1), data: configuration.parameters[parameter], value: (_b = parameters[parameter]) !== null && _b !== void 0 ? _b : configuration.parameters[parameter][0], onChange: (s) => setParameters(Object.assign(Object.assign({}, parameters), { [parameter]: s !== null && s !== void 0 ? s : configuration.parameters[parameter][0] })) }));
2603
+ }),
2591
2604
  React.createElement(BotSelector, { playerCount: playerCount, setPlayerCount: setPlayerCount, playerBots: playerBots, setPlayerBots: setPlayerBots, apis: apis }),
2592
2605
  React.createElement(core.NumberInput, { mt: "xs", leftSection: React.createElement("i", { className: "fa-solid fa-dice" }), label: "Randomness Seed", min: 0, value: seed, onChange: setSeed }),
2593
2606
  React.createElement(core.Button.Group, { mt: "xs" },
@@ -2775,7 +2788,7 @@ const NotFoundPage = () => {
2775
2788
  React.createElement("p", null, "Make sure the URL is correct.")));
2776
2789
  };
2777
2790
 
2778
- let currentMap;
2791
+ let currentParameters;
2779
2792
  let currentPlayers;
2780
2793
  const Round = () => {
2781
2794
  const firestore$1 = useFirestore();
@@ -2791,9 +2804,9 @@ const Round = () => {
2791
2804
  key: "Player Bots",
2792
2805
  defaultValue: ["None", "None"],
2793
2806
  });
2794
- const [map, setMap] = useLocalStorage({
2795
- key: "Map",
2796
- defaultValue: configuration.maps[0],
2807
+ const [parameters, setParameters] = useLocalStorage({
2808
+ key: "Parameters",
2809
+ defaultValue: {},
2797
2810
  });
2798
2811
  const [rounds, setRounds] = useLocalStorage({
2799
2812
  key: "Rounds",
@@ -2833,9 +2846,17 @@ const Round = () => {
2833
2846
  }
2834
2847
  React.useEffect(() => {
2835
2848
  if (remaining > 0) {
2836
- runNoUI(currentMap, apis, currentPlayers, "", false);
2849
+ runNoUI(currentParameters, apis, currentPlayers, "", false);
2837
2850
  }
2838
2851
  }, [remaining]);
2852
+ const getFullParameters = () => {
2853
+ var _a;
2854
+ const result = {};
2855
+ for (const key in configuration.parameters) {
2856
+ result[key] = (_a = parameters[key]) !== null && _a !== void 0 ? _a : configuration.parameters[key][0];
2857
+ }
2858
+ return result;
2859
+ };
2839
2860
  React.useEffect(updatePointModifier, [results]);
2840
2861
  return (React.createElement(React.Fragment, null,
2841
2862
  React.createElement("div", { style: {
@@ -2848,7 +2869,7 @@ const Round = () => {
2848
2869
  React.createElement("table", { style: { textAlign: "center" } },
2849
2870
  React.createElement("thead", null,
2850
2871
  React.createElement("tr", null,
2851
- React.createElement("th", { style: { width: "25%" } }, "Map"),
2872
+ React.createElement("th", { style: { width: "25%" } }, "Parameters"),
2852
2873
  React.createElement("th", { style: { width: "35%" } }, "Players"),
2853
2874
  roundIterations === 1 && (React.createElement(React.Fragment, null,
2854
2875
  React.createElement("th", { style: { width: "17.5%" } }, "1st Place"),
@@ -2858,7 +2879,7 @@ const Round = () => {
2858
2879
  React.createElement("tbody", null, rounds.map((round, index) => {
2859
2880
  const winCounts = {};
2860
2881
  if (results[round.players.join(", ")] !== undefined) {
2861
- const currentResults = results[round.players.join(", ")][round.map];
2882
+ const currentResults = results[round.players.join(", ")][JSON.stringify(round.parameters)];
2862
2883
  for (const result of currentResults) {
2863
2884
  const winner = round.players[result[0]];
2864
2885
  if (!winCounts[winner]) {
@@ -2871,28 +2892,31 @@ const Round = () => {
2871
2892
  }
2872
2893
  const winners = Object.keys(winCounts).sort((a, b) => winCounts[b] - winCounts[a]);
2873
2894
  return (React.createElement("tr", { key: index },
2874
- React.createElement("td", null, round.map),
2895
+ React.createElement("td", { style: { whiteSpace: "pre" } }, Object.keys(round.parameters)
2896
+ .sort()
2897
+ .map((p) => `${p[0].toUpperCase()}${p.slice(1)}: ${round.parameters[p]}`)
2898
+ .join("\n")),
2875
2899
  React.createElement("td", null, round.players.map((player, index) => (React.createElement("img", { key: index, src: `/images/teams/${player.toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } })))),
2876
2900
  roundIterations === 1 && (React.createElement(React.Fragment, null,
2877
2901
  React.createElement("td", null, results[round.players.join(", ")] !== undefined &&
2878
- results[round.players.join(", ")][round.map] !==
2879
- undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][round.map][0][0]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))),
2902
+ results[round.players.join(", ")][JSON.stringify(round.parameters)] !== undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][JSON.stringify(round.parameters)][0][0]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))),
2880
2903
  React.createElement("td", null, results[round.players.join(", ")] !== undefined &&
2881
- results[round.players.join(", ")][round.map] !==
2882
- undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][round.map][0][1]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))))),
2904
+ results[round.players.join(", ")][JSON.stringify(round.parameters)] !== undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][JSON.stringify(round.parameters)][0][1]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))))),
2883
2905
  roundIterations !== 1 && (React.createElement("td", null, winners.map((winner, index) => (React.createElement("p", { key: index },
2884
2906
  winner,
2885
2907
  ": ",
2886
2908
  winCounts[winner]))))),
2887
2909
  React.createElement("td", null,
2888
2910
  React.createElement(core.Button.Group, { orientation: "vertical" },
2889
- React.createElement(core.Button, { leftSection: React.createElement("i", { className: "fa-solid fa-play" }), size: "xs", onClick: () => navigate(`/simulation/${round.map.replaceAll(" ", "-")}/${round.players.map(encodeURIComponent).join(",")}?showcase=true`) }, "Simulate"),
2911
+ React.createElement(core.Button, { leftSection: React.createElement("i", { className: "fa-solid fa-play" }), size: "xs", onClick: () => navigate(`/simulation/${round.players.map(encodeURIComponent).join(",")}?showcase=true&${Object.keys(round.parameters)
2912
+ .map((p) => `${p}=${encodeURIComponent(round.parameters[p])}`)
2913
+ .join("&")}`) }, "Simulate"),
2890
2914
  React.createElement(core.Button, { leftSection: React.createElement("i", { className: "fa-solid fa-forward" }), size: "xs", onClick: () => {
2891
2915
  if (roundIterations === 1) {
2892
- runNoUI(round.map, apis, round.players, "", false);
2916
+ runNoUI(round.parameters, apis, round.players, "", false);
2893
2917
  }
2894
2918
  else {
2895
- currentMap = round.map;
2919
+ currentParameters = round.parameters;
2896
2920
  currentPlayers = round.players;
2897
2921
  startRunNoUIN(roundIterations);
2898
2922
  }
@@ -2903,12 +2927,14 @@ const Round = () => {
2903
2927
  "Remaining Simulations: ",
2904
2928
  remaining)),
2905
2929
  location.search.includes("edit") && (React.createElement(React.Fragment, null,
2906
- React.createElement(core.NumberInput, { value: roundIterations, onChange: (v) => { var _a; return setRoundIterations((_a = parseInt(v.toString(), 10)) !== null && _a !== void 0 ? _a : 1); }, label: "Round Iterations", leftSection: React.createElement("i", { className: "fa-solid fa-hashtag" }) }),
2907
- React.createElement(core.Select, { mt: "xs", leftSection: React.createElement("i", { className: "fa-solid fa-map" }), label: "Map", data: configuration.maps, value: map, onChange: (s) => {
2908
- if (s) {
2909
- setMap(s);
2910
- }
2911
- } }),
2930
+ React.createElement(core.NumberInput, { mb: "xs", value: roundIterations, onChange: (v) => { var _a; return setRoundIterations((_a = parseInt(v.toString(), 10)) !== null && _a !== void 0 ? _a : 1); }, label: "Round Iterations", leftSection: React.createElement("i", { className: "fa-solid fa-hashtag" }) }),
2931
+ Object.keys(configuration.parameters)
2932
+ .sort()
2933
+ .map((parameter, parameterIndex) => {
2934
+ var _a, _b;
2935
+ const icon = ((_a = configuration.parameterIcons) !== null && _a !== void 0 ? _a : {})[parameter];
2936
+ return (React.createElement(core.Select, { key: parameterIndex, mb: "xs", leftSection: icon ? React.createElement("i", { className: icon }) : undefined, label: parameter[0].toUpperCase() + parameter.slice(1), data: configuration.parameters[parameter], value: (_b = parameters[parameter]) !== null && _b !== void 0 ? _b : configuration.parameters[parameter][0], onChange: (s) => setParameters(Object.assign(Object.assign({}, parameters), { [parameter]: s !== null && s !== void 0 ? s : configuration.parameters[parameter][0] })) }));
2937
+ }),
2912
2938
  React.createElement(BotSelector, { playerCount: playerCount, setPlayerCount: setPlayerCount, playerBots: playerBots, setPlayerBots: setPlayerBots, apis: apis }),
2913
2939
  React.createElement(core.Button, { color: "red", leftSection: React.createElement("i", { className: "fa-solid fa-trash" }), mt: "xs", onClick: () => {
2914
2940
  setRounds([]);
@@ -2920,7 +2946,10 @@ const Round = () => {
2920
2946
  React.createElement(core.Button, { leftSection: React.createElement("i", { className: "fa-solid fa-plus" }), mt: "xs", onClick: () => {
2921
2947
  setRounds((rounds) => [
2922
2948
  ...rounds,
2923
- { players: playerBots, map },
2949
+ {
2950
+ players: playerBots,
2951
+ parameters: Object.assign({}, getFullParameters()),
2952
+ },
2924
2953
  ]);
2925
2954
  }, style: { width: "30%" } }, "Add"),
2926
2955
  React.createElement(core.Button, { leftSection: React.createElement("i", { className: "fa-solid fa-save" }), fullWidth: true, mt: "xs", onClick: () => __awaiter(void 0, void 0, void 0, function* () {
@@ -2940,7 +2969,7 @@ const Round = () => {
2940
2969
  80, 80, 80, 80,
2941
2970
  ]) {
2942
2971
  setRounds((rounds) => rounds.map((round) => ({
2943
- map: round.map,
2972
+ parameters: round.parameters,
2944
2973
  players: shuffle(round.players),
2945
2974
  })));
2946
2975
  yield new Promise((resolve) => setTimeout(resolve, timeout));
@@ -3108,7 +3137,7 @@ const Simulation = () => {
3108
3137
  var _a, _b, _c, _d;
3109
3138
  const admin = useAdmin();
3110
3139
  const [apis, loading] = useAPIs();
3111
- let { map, playerapis } = reactRouterDom.useParams();
3140
+ let { playerapis } = reactRouterDom.useParams();
3112
3141
  const location = reactRouterDom.useLocation();
3113
3142
  const [searchParams] = reactRouterDom.useSearchParams();
3114
3143
  const [winner, setWinner] = React.useState();
@@ -3147,7 +3176,10 @@ const Simulation = () => {
3147
3176
  }
3148
3177
  };
3149
3178
  }, []);
3150
- map = map === null || map === void 0 ? void 0 : map.split("&")[0].replaceAll("-", " ");
3179
+ const parameters = Object.fromEntries(searchParams);
3180
+ if (parameters.seed) {
3181
+ delete parameters.seed;
3182
+ }
3151
3183
  playerapis = playerapis === null || playerapis === void 0 ? void 0 : playerapis.split("&")[0];
3152
3184
  const playerNames = (_a = playerapis === null || playerapis === void 0 ? void 0 : playerapis.split(",").map(decodeURIComponent)) !== null && _a !== void 0 ? _a : [];
3153
3185
  const players = playerNames.map((api) => (api === "None" ? "" : apis[api]));
@@ -3161,7 +3193,7 @@ const Simulation = () => {
3161
3193
  const seed = (_a = searchParams.get("seed")) !== null && _a !== void 0 ? _a : "";
3162
3194
  tryUntilSuccess(() =>
3163
3195
  // @ts-ignore
3164
- window._startSimulation(map, players, playerNames, false, !showcaseMode, true, seed));
3196
+ window._startSimulation(parameters, players, playerNames, false, !showcaseMode, true, seed));
3165
3197
  }
3166
3198
  }, [loading]);
3167
3199
  const newRank = getRank(getLocalStorage("Cached tournament/info"), winner, getLocalStorage("Point Modifier")) + 1;
@@ -3314,7 +3346,7 @@ const App = ({ routes, blocks }) => {
3314
3346
  React.createElement(reactRouterDom.Routes, null,
3315
3347
  React.createElement(reactRouterDom.Route, { path: "/", element: React.createElement(HomePage, { blocks: blocks }) }),
3316
3348
  React.createElement(reactRouterDom.Route, { path: "/view/:apiname", element: React.createElement(ViewAPI, null) }),
3317
- React.createElement(reactRouterDom.Route, { path: "/simulation/:map/:playerapis", element: React.createElement(Simulation, null) }),
3349
+ React.createElement(reactRouterDom.Route, { path: "/simulation/:playerapis", element: React.createElement(Simulation, null) }),
3318
3350
  React.createElement(reactRouterDom.Route, { path: "/round", element: React.createElement(Round, null) }),
3319
3351
  React.createElement(reactRouterDom.Route, { path: "/settings", element: React.createElement(SettingsPage, null) }),
3320
3352
  React.createElement(reactRouterDom.Route, { path: "*", element: React.createElement(NotFoundPage, null) }),
@@ -5718,15 +5750,15 @@ const initialize = () => {
5718
5750
  }
5719
5751
  };
5720
5752
  // @ts-ignore
5721
- window.setResults = (playerNames, places, map, verbose) => {
5753
+ window.setResults = (playerNames, places, parameters, verbose) => {
5722
5754
  const results = getLocalStorage("Results");
5723
5755
  if (!results[playerNames.join(", ")]) {
5724
5756
  results[playerNames.join(", ")] = {};
5725
5757
  }
5726
- if (!results[playerNames.join(", ")][map]) {
5727
- results[playerNames.join(", ")][map] = [];
5758
+ if (!results[playerNames.join(", ")][JSON.stringify(parameters)]) {
5759
+ results[playerNames.join(", ")][JSON.stringify(parameters)] = [];
5728
5760
  }
5729
- results[playerNames.join(", ")][map].push(places);
5761
+ results[playerNames.join(", ")][JSON.stringify(parameters)].push(places);
5730
5762
  setLocalStorage("Results", results);
5731
5763
  updatePointModifier();
5732
5764
  // @ts-ignore
@@ -3,7 +3,7 @@ import type { Auth } from "firebase/auth";
3
3
  import type { Firestore } from "firebase/firestore";
4
4
  interface CodeBattlesRequiredConfiguration {
5
5
  firebase: any;
6
- maps: string[];
6
+ parameters: Record<string, string[]>;
7
7
  }
8
8
  interface CodeBattlesDatesConfiguration {
9
9
  locale?: string;
@@ -19,6 +19,7 @@ interface CodeBattlesOptionalConfiguration {
19
19
  authentication: Auth;
20
20
  runningCountOptions?: number[];
21
21
  dates?: CodeBattlesDatesConfiguration;
22
+ parameterIcons?: Record<string, string>;
22
23
  }
23
24
  export interface CodeBattlesConfiguration extends CodeBattlesRequiredConfiguration, Partial<CodeBattlesOptionalConfiguration> {
24
25
  }
@@ -1,6 +1,6 @@
1
1
  export interface RoundInfo {
2
2
  players: string[];
3
- map: string;
3
+ parameters: Record<string, string>;
4
4
  }
5
5
  export declare const getLocalStorage: (key: string, defaultValue?: {}) => any;
6
6
  export declare const setLocalStorage: (key: string, value?: {}) => void;
@@ -10,6 +10,6 @@ export declare const getRank: (tournamentInfo: any, team: string, pointModifier:
10
10
  export declare const updatePointModifier: () => void;
11
11
  export declare const toPlacing: (n: number) => string;
12
12
  export declare const zeroPad: (s: string, l: number) => string;
13
- export declare const runNoUI: (map: string, apis: Record<string, any>, playerBots: string[], seed: string, verbose: boolean) => void;
13
+ export declare const runNoUI: (parameters: Record<string, string>, apis: Record<string, any>, playerBots: string[], seed: string, verbose: boolean) => void;
14
14
  export declare const tryUntilSuccess: (f: () => void, timeout?: number) => void;
15
15
  export declare const downloadFile: (filename: string, mimeType: string, contents: string) => void;
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
 
3
- from code_battles.utilities import GameCanvas, Alignment, is_web, is_worker
4
3
  from code_battles.battles import CodeBattles
4
+ from code_battles.utilities import Alignment, GameCanvas, is_web, is_worker
5
5
 
6
6
 
7
7
  def run_game(battles: CodeBattles):
@@ -1,29 +1,29 @@
1
1
  import asyncio
2
2
  import base64
3
- from dataclasses import dataclass
4
3
  import datetime
4
+ import gzip
5
5
  import json
6
6
  import math
7
- import time
8
- from random import Random
9
7
  import sys
8
+ import time
10
9
  import traceback
11
- import gzip
10
+ import typing
11
+ from dataclasses import dataclass
12
+ from random import Random
13
+ from typing import Any, Dict, Generic, List, Optional, Set, Tuple, TypeVar
12
14
  from urllib.parse import quote
13
15
 
14
- from typing import Any, Dict, Generic, List, Optional, Set, Tuple, TypeVar
15
- import typing
16
16
  from code_battles.utilities import (
17
17
  GameCanvas,
18
18
  console_log,
19
19
  download_image,
20
+ is_web,
20
21
  is_worker,
21
22
  navigate,
22
23
  set_results,
23
24
  show_alert,
24
25
  show_download,
25
26
  web_only,
26
- is_web,
27
27
  )
28
28
 
29
29
  try:
@@ -39,7 +39,7 @@ PlayerRequestsType = TypeVar("PlayerRequestsType")
39
39
 
40
40
  @dataclass
41
41
  class Simulation:
42
- map: str
42
+ parameters: Dict[str, str]
43
43
  player_names: str
44
44
  game: str
45
45
  version: str
@@ -53,7 +53,7 @@ class Simulation:
53
53
  gzip.compress(
54
54
  json.dumps(
55
55
  {
56
- "map": self.map,
56
+ "parameters": self.parameters,
57
57
  "playerNames": self.player_names,
58
58
  "game": self.game,
59
59
  "version": self.version,
@@ -73,7 +73,9 @@ class Simulation:
73
73
  def load(file: str):
74
74
  contents: Dict[str, Any] = json.loads(gzip.decompress(base64.b64decode(file)))
75
75
  return Simulation(
76
- contents["map"],
76
+ contents["parameters"]
77
+ if "parameters" in contents
78
+ else {"map": contents["map"]},
77
79
  contents["playerNames"],
78
80
  contents["game"],
79
81
  contents["version"],
@@ -105,6 +107,8 @@ class CodeBattles(
105
107
 
106
108
  player_names: List[str]
107
109
  """The name of the players. This is populated before any of the overridable methods run."""
110
+ parameters: Dict[str, str]
111
+ """The parameters of the simulation. This is populated before any of the overridable methods run."""
108
112
  map: str
109
113
  """The name of the map. This is populated before any of the overridable methods run."""
110
114
  map_image: "js.Image"
@@ -358,12 +362,13 @@ class CodeBattles(
358
362
  await ff.load()
359
363
  document.fonts.add(ff)
360
364
 
361
- def run_bot_method(self, player_index: int, method_name: str):
365
+ def run_bot_method(self, player_index: int, method_name: str) -> float:
362
366
  """
363
- Runs the specifid method of the given player.
367
+ Runs the specified method of the given player and returns the time it took (in seconds).
364
368
 
365
369
  Upon exception, shows an alert (does not terminate the bot).
366
370
  """
371
+ start = time.time()
367
372
 
368
373
  assert player_index in self.active_players
369
374
 
@@ -392,6 +397,9 @@ class CodeBattles(
392
397
  "fa-solid fa-exclamation",
393
398
  )
394
399
 
400
+ end = time.time()
401
+ return end - start
402
+
395
403
  def eliminate_player(self, player_index: int, reason=""):
396
404
  """Eliminate the specified player for the specified reason from the simulation."""
397
405
 
@@ -468,7 +476,7 @@ class CodeBattles(
468
476
 
469
477
  If ``force`` is set, will play the sound even if the simulation is not :attr:`verbose`.
470
478
  """
471
- from js import window, Audio
479
+ from js import Audio, window
472
480
 
473
481
  if not force and not self.verbose:
474
482
  return
@@ -521,7 +529,7 @@ class CodeBattles(
521
529
 
522
530
  @web_only
523
531
  def _initialize(self):
524
- from js import window, document
532
+ from js import document, window
525
533
  from pyscript.ffi import create_proxy
526
534
 
527
535
  window.addEventListener("resize", create_proxy(lambda _: self._resize_canvas()))
@@ -565,7 +573,7 @@ class CodeBattles(
565
573
 
566
574
  def _run_webworker_simulation(
567
575
  self,
568
- map: str,
576
+ parameters: Dict[str, str],
569
577
  player_names_str: str,
570
578
  player_codes_str: str,
571
579
  seed: Optional[int] = None,
@@ -573,10 +581,12 @@ class CodeBattles(
573
581
  from pyscript import sync
574
582
 
575
583
  # JS to Python
584
+ parameters = json.loads(parameters)
576
585
  player_names = json.loads(player_names_str)
577
586
  player_codes = json.loads(player_codes_str)
578
587
 
579
- self.map = map
588
+ self.parameters = parameters
589
+ self.map = parameters.get("map")
580
590
  self.player_names = player_names
581
591
  self.background = True
582
592
  self.console_visible = False
@@ -589,7 +599,10 @@ class CodeBattles(
589
599
  decisions = self._make_decisions()
590
600
  logs = self._logs
591
601
  alerts = self._alerts
602
+ log = self.log
603
+ self.log = lambda *args, **kwargs: None
592
604
  self.apply_decisions(decisions)
605
+ self.log = log
593
606
 
594
607
  sync.update_step(
595
608
  base64.b64encode(decisions).decode(),
@@ -620,7 +633,8 @@ class CodeBattles(
620
633
  contents = f.read()
621
634
  simulation = Simulation.load(contents)
622
635
  seed = simulation.seed
623
- self.map = simulation.map
636
+ self.parameters = simulation.parameters
637
+ self.map = simulation.parameters.get("map")
624
638
  self.player_names = simulation.player_names
625
639
  decisions = simulation.decisions
626
640
  player_codes = ["" for _ in simulation.player_names]
@@ -684,12 +698,15 @@ class CodeBattles(
684
698
 
685
699
  try:
686
700
  simulation = Simulation.load(str(contents))
701
+ parameters = "&".join(
702
+ [f"{p}={quote(v)}" for p, v in simulation.parameters.items()]
703
+ )
687
704
  navigate(
688
- f"/simulation/{simulation.map}/{','.join([quote(player_name) for player_name in simulation.player_names])}?seed={simulation.seed}"
705
+ f"/simulation/{','.join([quote(player_name) for player_name in simulation.player_names])}?seed={simulation.seed}&{parameters}"
689
706
  )
690
707
  self.alert(
691
708
  "Loaded simulation file!",
692
- f"{', '.join(simulation.player_names)} competed in {simulation.map} at {simulation.timestamp}",
709
+ f"{', '.join(simulation.player_names)} competed with {simulation.parameters} at {simulation.timestamp}",
693
710
  "blue",
694
711
  "fa-solid fa-file-code",
695
712
  0,
@@ -713,9 +730,10 @@ class CodeBattles(
713
730
  while document.getElementById("loader") is None:
714
731
  await asyncio.sleep(0.01)
715
732
  self._initialize()
716
- self.map = simulation.map
733
+ self.parameters = simulation.parameters
734
+ self.map = self.parameters.get("map")
717
735
  self.map_image = await download_image(
718
- self.configure_map_image_url(simulation.map)
736
+ self.configure_map_image_url(self.map)
719
737
  )
720
738
  self.player_names = simulation.player_names
721
739
  self.background = False
@@ -744,7 +762,7 @@ class CodeBattles(
744
762
  @web_only
745
763
  async def _start_simulation_async(
746
764
  self,
747
- map: str,
765
+ parameters: Dict[str, str],
748
766
  player_codes: List[str],
749
767
  player_names: List[str],
750
768
  background: bool,
@@ -758,15 +776,19 @@ class CodeBattles(
758
776
  # JS to Python
759
777
  player_names = [str(x) for x in player_names]
760
778
  player_codes = [str(x) for x in player_codes]
779
+ parameters = parameters.to_py()
761
780
 
762
781
  try:
763
782
  render_status = document.getElementById("render-status")
764
783
  if render_status is not None:
765
784
  render_status.textContent = "Rendering: Initializing..."
766
785
 
767
- self.map = map
786
+ self.parameters = parameters
787
+ self.map = parameters.get("map")
768
788
  self.player_names = player_names
769
- self.map_image = await download_image(self.configure_map_image_url(map))
789
+ self.map_image = await download_image(
790
+ self.configure_map_image_url(self.map)
791
+ )
770
792
  self.background = background
771
793
  self.console_visible = console_visible
772
794
  self.verbose = verbose
@@ -794,7 +816,10 @@ class CodeBattles(
794
816
  self._worker = await workers["worker"]
795
817
  self._worker.update_step = self._update_step
796
818
  self._worker._run_webworker_simulation(
797
- map, json.dumps(player_names), json.dumps(player_codes), self._seed
819
+ json.dumps(parameters),
820
+ json.dumps(player_names),
821
+ json.dumps(player_codes),
822
+ self._seed,
798
823
  )
799
824
 
800
825
  if self.background:
@@ -824,7 +849,7 @@ class CodeBattles(
824
849
 
825
850
  def _get_simulation(self):
826
851
  return Simulation(
827
- self.map,
852
+ self.parameters,
828
853
  self.player_names,
829
854
  self.__class__.__name__,
830
855
  self.configure_version(),
@@ -842,7 +867,7 @@ class CodeBattles(
842
867
  is_over_str: str,
843
868
  should_pause_str: str,
844
869
  ):
845
- from js import window, document
870
+ from js import document, window
846
871
 
847
872
  now = time.time()
848
873
  decisions = base64.b64decode(str(decisions_str))
@@ -1020,7 +1045,7 @@ class CodeBattles(
1020
1045
  if len(self.active_players) == 1:
1021
1046
  self._eliminated.append(self.active_players[0])
1022
1047
  set_results(
1023
- self.player_names, self._eliminated[::-1], self.map, self.verbose
1048
+ self.player_names, self._eliminated[::-1], self.parameters, self.verbose
1024
1049
  )
1025
1050
  if not self.background:
1026
1051
  self.render()
@@ -3,9 +3,9 @@
3
3
  import asyncio
4
4
  import math
5
5
  import sys
6
- from typing import Callable, List, Union
7
6
  from enum import Enum
8
7
  from functools import wraps
8
+ from typing import Callable, Dict, List, Union
9
9
 
10
10
  try:
11
11
  import js
@@ -73,12 +73,17 @@ def show_alert(
73
73
 
74
74
 
75
75
  @web_only
76
- def set_results(player_names: List[str], places: List[int], map: str, verbose: bool):
76
+ def set_results(
77
+ player_names: List[str],
78
+ places: List[int],
79
+ parameters: Dict[str, str],
80
+ verbose: bool,
81
+ ):
77
82
  from js import window
78
83
 
79
84
  if hasattr(window, "setResults"):
80
85
  try:
81
- window.setResults(player_names, places, map, verbose)
86
+ window.setResults(player_names, places, parameters, verbose)
82
87
  except Exception as e:
83
88
  print(e)
84
89
 
package/dist/esm/index.js CHANGED
@@ -372,8 +372,8 @@ const updatePointModifier = () => {
372
372
  const pointModifier = {};
373
373
  for (const round of rounds) {
374
374
  if (results[round.players.join(", ")] &&
375
- results[round.players.join(", ")][round.map]) {
376
- for (const result of results[round.players.join(", ")][round.map]) {
375
+ results[round.players.join(", ")][JSON.stringify(round.parameters)]) {
376
+ for (const result of results[round.players.join(", ")][JSON.stringify(round.parameters)]) {
377
377
  const first = round.players[result[0]];
378
378
  const second = round.players[result[1]];
379
379
  if (!pointModifier[first]) {
@@ -401,11 +401,11 @@ const toPlacing = (n) => {
401
401
  }
402
402
  return n.toString() + DIGITS[n % 10];
403
403
  };
404
- const runNoUI = (map, apis, playerBots, seed, verbose) => {
404
+ const runNoUI = (parameters, apis, playerBots, seed, verbose) => {
405
405
  const players = playerBots.map((api) => (api === "None" ? "" : apis[api]));
406
406
  tryUntilSuccess(() =>
407
407
  // @ts-ignore
408
- window._startSimulation(map, players, playerBots, true, false, verbose, seed));
408
+ window._startSimulation(parameters, players, playerBots, true, false, verbose, seed));
409
409
  };
410
410
  const tryUntilSuccess = (f, timeout = 500) => {
411
411
  try {
@@ -2464,7 +2464,7 @@ const PickBotBlock = () => {
2464
2464
 
2465
2465
  const BotSelector = ({ playerCount, setPlayerCount, playerBots, setPlayerBots, apis, }) => {
2466
2466
  return (React.createElement(React.Fragment, null,
2467
- React.createElement(NumberInput, { mt: "xs", label: "Player Count", value: playerCount, min: 1, onChange: (c) => {
2467
+ React.createElement(NumberInput, { label: "Player Count", value: playerCount, min: 1, onChange: (c) => {
2468
2468
  if (typeof c === "number") {
2469
2469
  setPlayerCount(c);
2470
2470
  while (playerBots.length < c) {
@@ -2487,9 +2487,9 @@ const BotSelector = ({ playerCount, setPlayerCount, playerBots, setPlayerBots, a
2487
2487
  const RunSimulationBlock = () => {
2488
2488
  const [apis, loading] = useAPIs();
2489
2489
  const configuration = useConfiguration();
2490
- const [map, setMap] = useLocalStorage({
2491
- key: "Map",
2492
- defaultValue: configuration.maps[0],
2490
+ const [parameters, setParameters] = useLocalStorage({
2491
+ key: "Parameters",
2492
+ defaultValue: {},
2493
2493
  });
2494
2494
  const [playerCount, setPlayerCount] = useLocalStorage({
2495
2495
  key: "Player Count",
@@ -2510,19 +2510,30 @@ const RunSimulationBlock = () => {
2510
2510
  if (Object.keys(runningNoUIN).length === 1) {
2511
2511
  remaining = Math.max(...Object.values(runningNoUIN));
2512
2512
  }
2513
+ const getFullParameters = () => {
2514
+ var _a;
2515
+ const result = {};
2516
+ for (const key in configuration.parameters) {
2517
+ result[key] = (_a = parameters[key]) !== null && _a !== void 0 ? _a : configuration.parameters[key][0];
2518
+ }
2519
+ return result;
2520
+ };
2513
2521
  const run = () => {
2514
2522
  // @ts-ignore
2515
2523
  window._isSimulationFromFile = false;
2516
- navigate(`/simulation/${map.replaceAll(" ", "-")}/${playerBots.map(encodeURIComponent).join(",")}?seed=${seed === "-" ? "" : seed}`);
2524
+ const params = getFullParameters();
2525
+ navigate(`/simulation/${playerBots.map(encodeURIComponent).join(",")}?seed=${seed === "-" ? "" : seed}&${Object.keys(params)
2526
+ .map((p) => `${p}=${encodeURIComponent(params[p])}`)
2527
+ .join("&")}`);
2517
2528
  };
2518
2529
  const startRunNoUI = () => {
2519
2530
  setRunningNoUI(true);
2520
- runNoUI(map, apis, playerBots, seed.toString(), false);
2531
+ runNoUI(getFullParameters(), apis, playerBots, seed.toString(), false);
2521
2532
  };
2522
2533
  const startRunNoUIN = (n) => {
2523
2534
  setLocalStorage("Results", {});
2524
2535
  setRunningNoUIN({ [n.toString()]: n });
2525
- runNoUI(map, apis, playerBots, seed.toString(), false);
2536
+ runNoUI(getFullParameters(), apis, playerBots, seed.toString(), false);
2526
2537
  };
2527
2538
  useEffect(() => {
2528
2539
  // @ts-ignore
@@ -2551,7 +2562,7 @@ const RunSimulationBlock = () => {
2551
2562
  for (const key in runningNoUIN) {
2552
2563
  if (runningNoUIN[key] == 0) {
2553
2564
  const results = getLocalStorage("Results");
2554
- const currentResults = results[playerBots.join(", ")][map];
2565
+ const currentResults = results[playerBots.join(", ")][JSON.stringify(getFullParameters())];
2555
2566
  const winCounts = {};
2556
2567
  for (const result of currentResults) {
2557
2568
  const winner = playerBots[result[0]];
@@ -2576,16 +2587,18 @@ const RunSimulationBlock = () => {
2576
2587
  setLocalStorage("Results", {});
2577
2588
  }
2578
2589
  else {
2579
- runNoUI(map, apis, playerBots, seed.toString(), false);
2590
+ runNoUI(getFullParameters(), apis, playerBots, seed.toString(), false);
2580
2591
  }
2581
2592
  }
2582
2593
  }, [runningNoUIN]);
2583
2594
  return (React.createElement(Block, { title: "Run Simulation", logo: "fa-solid fa-display" },
2584
- React.createElement(Select, { leftSection: React.createElement("i", { className: "fa-solid fa-earth-americas" }), label: "Map", data: configuration.maps, value: map, onChange: (s) => {
2585
- if (s) {
2586
- setMap(s);
2587
- }
2588
- } }),
2595
+ Object.keys(configuration.parameters)
2596
+ .sort()
2597
+ .map((parameter, parameterIndex) => {
2598
+ var _a, _b;
2599
+ const icon = ((_a = configuration.parameterIcons) !== null && _a !== void 0 ? _a : {})[parameter];
2600
+ return (React.createElement(Select, { mb: "xs", key: parameterIndex, leftSection: icon ? React.createElement("i", { className: icon }) : undefined, label: parameter[0].toUpperCase() + parameter.slice(1), data: configuration.parameters[parameter], value: (_b = parameters[parameter]) !== null && _b !== void 0 ? _b : configuration.parameters[parameter][0], onChange: (s) => setParameters(Object.assign(Object.assign({}, parameters), { [parameter]: s !== null && s !== void 0 ? s : configuration.parameters[parameter][0] })) }));
2601
+ }),
2589
2602
  React.createElement(BotSelector, { playerCount: playerCount, setPlayerCount: setPlayerCount, playerBots: playerBots, setPlayerBots: setPlayerBots, apis: apis }),
2590
2603
  React.createElement(NumberInput, { mt: "xs", leftSection: React.createElement("i", { className: "fa-solid fa-dice" }), label: "Randomness Seed", min: 0, value: seed, onChange: setSeed }),
2591
2604
  React.createElement(Button.Group, { mt: "xs" },
@@ -2773,7 +2786,7 @@ const NotFoundPage = () => {
2773
2786
  React.createElement("p", null, "Make sure the URL is correct.")));
2774
2787
  };
2775
2788
 
2776
- let currentMap;
2789
+ let currentParameters;
2777
2790
  let currentPlayers;
2778
2791
  const Round = () => {
2779
2792
  const firestore = useFirestore();
@@ -2789,9 +2802,9 @@ const Round = () => {
2789
2802
  key: "Player Bots",
2790
2803
  defaultValue: ["None", "None"],
2791
2804
  });
2792
- const [map, setMap] = useLocalStorage({
2793
- key: "Map",
2794
- defaultValue: configuration.maps[0],
2805
+ const [parameters, setParameters] = useLocalStorage({
2806
+ key: "Parameters",
2807
+ defaultValue: {},
2795
2808
  });
2796
2809
  const [rounds, setRounds] = useLocalStorage({
2797
2810
  key: "Rounds",
@@ -2831,9 +2844,17 @@ const Round = () => {
2831
2844
  }
2832
2845
  useEffect(() => {
2833
2846
  if (remaining > 0) {
2834
- runNoUI(currentMap, apis, currentPlayers, "", false);
2847
+ runNoUI(currentParameters, apis, currentPlayers, "", false);
2835
2848
  }
2836
2849
  }, [remaining]);
2850
+ const getFullParameters = () => {
2851
+ var _a;
2852
+ const result = {};
2853
+ for (const key in configuration.parameters) {
2854
+ result[key] = (_a = parameters[key]) !== null && _a !== void 0 ? _a : configuration.parameters[key][0];
2855
+ }
2856
+ return result;
2857
+ };
2837
2858
  useEffect(updatePointModifier, [results]);
2838
2859
  return (React.createElement(React.Fragment, null,
2839
2860
  React.createElement("div", { style: {
@@ -2846,7 +2867,7 @@ const Round = () => {
2846
2867
  React.createElement("table", { style: { textAlign: "center" } },
2847
2868
  React.createElement("thead", null,
2848
2869
  React.createElement("tr", null,
2849
- React.createElement("th", { style: { width: "25%" } }, "Map"),
2870
+ React.createElement("th", { style: { width: "25%" } }, "Parameters"),
2850
2871
  React.createElement("th", { style: { width: "35%" } }, "Players"),
2851
2872
  roundIterations === 1 && (React.createElement(React.Fragment, null,
2852
2873
  React.createElement("th", { style: { width: "17.5%" } }, "1st Place"),
@@ -2856,7 +2877,7 @@ const Round = () => {
2856
2877
  React.createElement("tbody", null, rounds.map((round, index) => {
2857
2878
  const winCounts = {};
2858
2879
  if (results[round.players.join(", ")] !== undefined) {
2859
- const currentResults = results[round.players.join(", ")][round.map];
2880
+ const currentResults = results[round.players.join(", ")][JSON.stringify(round.parameters)];
2860
2881
  for (const result of currentResults) {
2861
2882
  const winner = round.players[result[0]];
2862
2883
  if (!winCounts[winner]) {
@@ -2869,28 +2890,31 @@ const Round = () => {
2869
2890
  }
2870
2891
  const winners = Object.keys(winCounts).sort((a, b) => winCounts[b] - winCounts[a]);
2871
2892
  return (React.createElement("tr", { key: index },
2872
- React.createElement("td", null, round.map),
2893
+ React.createElement("td", { style: { whiteSpace: "pre" } }, Object.keys(round.parameters)
2894
+ .sort()
2895
+ .map((p) => `${p[0].toUpperCase()}${p.slice(1)}: ${round.parameters[p]}`)
2896
+ .join("\n")),
2873
2897
  React.createElement("td", null, round.players.map((player, index) => (React.createElement("img", { key: index, src: `/images/teams/${player.toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } })))),
2874
2898
  roundIterations === 1 && (React.createElement(React.Fragment, null,
2875
2899
  React.createElement("td", null, results[round.players.join(", ")] !== undefined &&
2876
- results[round.players.join(", ")][round.map] !==
2877
- undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][round.map][0][0]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))),
2900
+ results[round.players.join(", ")][JSON.stringify(round.parameters)] !== undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][JSON.stringify(round.parameters)][0][0]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))),
2878
2901
  React.createElement("td", null, results[round.players.join(", ")] !== undefined &&
2879
- results[round.players.join(", ")][round.map] !==
2880
- undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][round.map][0][1]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))))),
2902
+ results[round.players.join(", ")][JSON.stringify(round.parameters)] !== undefined && (React.createElement("img", { src: `/images/teams/${round.players[results[round.players.join(", ")][JSON.stringify(round.parameters)][0][1]].toLowerCase()}.png`, width: 30, style: { marginInlineEnd: 10 } }))))),
2881
2903
  roundIterations !== 1 && (React.createElement("td", null, winners.map((winner, index) => (React.createElement("p", { key: index },
2882
2904
  winner,
2883
2905
  ": ",
2884
2906
  winCounts[winner]))))),
2885
2907
  React.createElement("td", null,
2886
2908
  React.createElement(Button.Group, { orientation: "vertical" },
2887
- React.createElement(Button, { leftSection: React.createElement("i", { className: "fa-solid fa-play" }), size: "xs", onClick: () => navigate(`/simulation/${round.map.replaceAll(" ", "-")}/${round.players.map(encodeURIComponent).join(",")}?showcase=true`) }, "Simulate"),
2909
+ React.createElement(Button, { leftSection: React.createElement("i", { className: "fa-solid fa-play" }), size: "xs", onClick: () => navigate(`/simulation/${round.players.map(encodeURIComponent).join(",")}?showcase=true&${Object.keys(round.parameters)
2910
+ .map((p) => `${p}=${encodeURIComponent(round.parameters[p])}`)
2911
+ .join("&")}`) }, "Simulate"),
2888
2912
  React.createElement(Button, { leftSection: React.createElement("i", { className: "fa-solid fa-forward" }), size: "xs", onClick: () => {
2889
2913
  if (roundIterations === 1) {
2890
- runNoUI(round.map, apis, round.players, "", false);
2914
+ runNoUI(round.parameters, apis, round.players, "", false);
2891
2915
  }
2892
2916
  else {
2893
- currentMap = round.map;
2917
+ currentParameters = round.parameters;
2894
2918
  currentPlayers = round.players;
2895
2919
  startRunNoUIN(roundIterations);
2896
2920
  }
@@ -2901,12 +2925,14 @@ const Round = () => {
2901
2925
  "Remaining Simulations: ",
2902
2926
  remaining)),
2903
2927
  location.search.includes("edit") && (React.createElement(React.Fragment, null,
2904
- React.createElement(NumberInput, { value: roundIterations, onChange: (v) => { var _a; return setRoundIterations((_a = parseInt(v.toString(), 10)) !== null && _a !== void 0 ? _a : 1); }, label: "Round Iterations", leftSection: React.createElement("i", { className: "fa-solid fa-hashtag" }) }),
2905
- React.createElement(Select, { mt: "xs", leftSection: React.createElement("i", { className: "fa-solid fa-map" }), label: "Map", data: configuration.maps, value: map, onChange: (s) => {
2906
- if (s) {
2907
- setMap(s);
2908
- }
2909
- } }),
2928
+ React.createElement(NumberInput, { mb: "xs", value: roundIterations, onChange: (v) => { var _a; return setRoundIterations((_a = parseInt(v.toString(), 10)) !== null && _a !== void 0 ? _a : 1); }, label: "Round Iterations", leftSection: React.createElement("i", { className: "fa-solid fa-hashtag" }) }),
2929
+ Object.keys(configuration.parameters)
2930
+ .sort()
2931
+ .map((parameter, parameterIndex) => {
2932
+ var _a, _b;
2933
+ const icon = ((_a = configuration.parameterIcons) !== null && _a !== void 0 ? _a : {})[parameter];
2934
+ return (React.createElement(Select, { key: parameterIndex, mb: "xs", leftSection: icon ? React.createElement("i", { className: icon }) : undefined, label: parameter[0].toUpperCase() + parameter.slice(1), data: configuration.parameters[parameter], value: (_b = parameters[parameter]) !== null && _b !== void 0 ? _b : configuration.parameters[parameter][0], onChange: (s) => setParameters(Object.assign(Object.assign({}, parameters), { [parameter]: s !== null && s !== void 0 ? s : configuration.parameters[parameter][0] })) }));
2935
+ }),
2910
2936
  React.createElement(BotSelector, { playerCount: playerCount, setPlayerCount: setPlayerCount, playerBots: playerBots, setPlayerBots: setPlayerBots, apis: apis }),
2911
2937
  React.createElement(Button, { color: "red", leftSection: React.createElement("i", { className: "fa-solid fa-trash" }), mt: "xs", onClick: () => {
2912
2938
  setRounds([]);
@@ -2918,7 +2944,10 @@ const Round = () => {
2918
2944
  React.createElement(Button, { leftSection: React.createElement("i", { className: "fa-solid fa-plus" }), mt: "xs", onClick: () => {
2919
2945
  setRounds((rounds) => [
2920
2946
  ...rounds,
2921
- { players: playerBots, map },
2947
+ {
2948
+ players: playerBots,
2949
+ parameters: Object.assign({}, getFullParameters()),
2950
+ },
2922
2951
  ]);
2923
2952
  }, style: { width: "30%" } }, "Add"),
2924
2953
  React.createElement(Button, { leftSection: React.createElement("i", { className: "fa-solid fa-save" }), fullWidth: true, mt: "xs", onClick: () => __awaiter(void 0, void 0, void 0, function* () {
@@ -2938,7 +2967,7 @@ const Round = () => {
2938
2967
  80, 80, 80, 80,
2939
2968
  ]) {
2940
2969
  setRounds((rounds) => rounds.map((round) => ({
2941
- map: round.map,
2970
+ parameters: round.parameters,
2942
2971
  players: shuffle(round.players),
2943
2972
  })));
2944
2973
  yield new Promise((resolve) => setTimeout(resolve, timeout));
@@ -3106,7 +3135,7 @@ const Simulation = () => {
3106
3135
  var _a, _b, _c, _d;
3107
3136
  const admin = useAdmin();
3108
3137
  const [apis, loading] = useAPIs();
3109
- let { map, playerapis } = useParams();
3138
+ let { playerapis } = useParams();
3110
3139
  const location = useLocation();
3111
3140
  const [searchParams] = useSearchParams();
3112
3141
  const [winner, setWinner] = useState();
@@ -3145,7 +3174,10 @@ const Simulation = () => {
3145
3174
  }
3146
3175
  };
3147
3176
  }, []);
3148
- map = map === null || map === void 0 ? void 0 : map.split("&")[0].replaceAll("-", " ");
3177
+ const parameters = Object.fromEntries(searchParams);
3178
+ if (parameters.seed) {
3179
+ delete parameters.seed;
3180
+ }
3149
3181
  playerapis = playerapis === null || playerapis === void 0 ? void 0 : playerapis.split("&")[0];
3150
3182
  const playerNames = (_a = playerapis === null || playerapis === void 0 ? void 0 : playerapis.split(",").map(decodeURIComponent)) !== null && _a !== void 0 ? _a : [];
3151
3183
  const players = playerNames.map((api) => (api === "None" ? "" : apis[api]));
@@ -3159,7 +3191,7 @@ const Simulation = () => {
3159
3191
  const seed = (_a = searchParams.get("seed")) !== null && _a !== void 0 ? _a : "";
3160
3192
  tryUntilSuccess(() =>
3161
3193
  // @ts-ignore
3162
- window._startSimulation(map, players, playerNames, false, !showcaseMode, true, seed));
3194
+ window._startSimulation(parameters, players, playerNames, false, !showcaseMode, true, seed));
3163
3195
  }
3164
3196
  }, [loading]);
3165
3197
  const newRank = getRank(getLocalStorage("Cached tournament/info"), winner, getLocalStorage("Point Modifier")) + 1;
@@ -3312,7 +3344,7 @@ const App = ({ routes, blocks }) => {
3312
3344
  React.createElement(Routes, null,
3313
3345
  React.createElement(Route, { path: "/", element: React.createElement(HomePage, { blocks: blocks }) }),
3314
3346
  React.createElement(Route, { path: "/view/:apiname", element: React.createElement(ViewAPI, null) }),
3315
- React.createElement(Route, { path: "/simulation/:map/:playerapis", element: React.createElement(Simulation, null) }),
3347
+ React.createElement(Route, { path: "/simulation/:playerapis", element: React.createElement(Simulation, null) }),
3316
3348
  React.createElement(Route, { path: "/round", element: React.createElement(Round, null) }),
3317
3349
  React.createElement(Route, { path: "/settings", element: React.createElement(SettingsPage, null) }),
3318
3350
  React.createElement(Route, { path: "*", element: React.createElement(NotFoundPage, null) }),
@@ -5716,15 +5748,15 @@ const initialize = () => {
5716
5748
  }
5717
5749
  };
5718
5750
  // @ts-ignore
5719
- window.setResults = (playerNames, places, map, verbose) => {
5751
+ window.setResults = (playerNames, places, parameters, verbose) => {
5720
5752
  const results = getLocalStorage("Results");
5721
5753
  if (!results[playerNames.join(", ")]) {
5722
5754
  results[playerNames.join(", ")] = {};
5723
5755
  }
5724
- if (!results[playerNames.join(", ")][map]) {
5725
- results[playerNames.join(", ")][map] = [];
5756
+ if (!results[playerNames.join(", ")][JSON.stringify(parameters)]) {
5757
+ results[playerNames.join(", ")][JSON.stringify(parameters)] = [];
5726
5758
  }
5727
- results[playerNames.join(", ")][map].push(places);
5759
+ results[playerNames.join(", ")][JSON.stringify(parameters)].push(places);
5728
5760
  setLocalStorage("Results", results);
5729
5761
  updatePointModifier();
5730
5762
  // @ts-ignore
@@ -3,7 +3,7 @@ import type { Auth } from "firebase/auth";
3
3
  import type { Firestore } from "firebase/firestore";
4
4
  interface CodeBattlesRequiredConfiguration {
5
5
  firebase: any;
6
- maps: string[];
6
+ parameters: Record<string, string[]>;
7
7
  }
8
8
  interface CodeBattlesDatesConfiguration {
9
9
  locale?: string;
@@ -19,6 +19,7 @@ interface CodeBattlesOptionalConfiguration {
19
19
  authentication: Auth;
20
20
  runningCountOptions?: number[];
21
21
  dates?: CodeBattlesDatesConfiguration;
22
+ parameterIcons?: Record<string, string>;
22
23
  }
23
24
  export interface CodeBattlesConfiguration extends CodeBattlesRequiredConfiguration, Partial<CodeBattlesOptionalConfiguration> {
24
25
  }
@@ -1,6 +1,6 @@
1
1
  export interface RoundInfo {
2
2
  players: string[];
3
- map: string;
3
+ parameters: Record<string, string>;
4
4
  }
5
5
  export declare const getLocalStorage: (key: string, defaultValue?: {}) => any;
6
6
  export declare const setLocalStorage: (key: string, value?: {}) => void;
@@ -10,6 +10,6 @@ export declare const getRank: (tournamentInfo: any, team: string, pointModifier:
10
10
  export declare const updatePointModifier: () => void;
11
11
  export declare const toPlacing: (n: number) => string;
12
12
  export declare const zeroPad: (s: string, l: number) => string;
13
- export declare const runNoUI: (map: string, apis: Record<string, any>, playerBots: string[], seed: string, verbose: boolean) => void;
13
+ export declare const runNoUI: (parameters: Record<string, string>, apis: Record<string, any>, playerBots: string[], seed: string, verbose: boolean) => void;
14
14
  export declare const tryUntilSuccess: (f: () => void, timeout?: number) => void;
15
15
  export declare const downloadFile: (filename: string, mimeType: string, contents: string) => void;
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import { Firestore } from 'firebase/firestore';
5
5
 
6
6
  interface CodeBattlesRequiredConfiguration {
7
7
  firebase: any;
8
- maps: string[];
8
+ parameters: Record<string, string[]>;
9
9
  }
10
10
  interface CodeBattlesDatesConfiguration {
11
11
  locale?: string;
@@ -21,6 +21,7 @@ interface CodeBattlesOptionalConfiguration {
21
21
  authentication: Auth;
22
22
  runningCountOptions?: number[];
23
23
  dates?: CodeBattlesDatesConfiguration;
24
+ parameterIcons?: Record<string, string>;
24
25
  }
25
26
  interface CodeBattlesConfiguration extends CodeBattlesRequiredConfiguration, Partial<CodeBattlesOptionalConfiguration> {
26
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-battles",
3
- "version": "1.6.4",
3
+ "version": "1.7.0",
4
4
  "description": "A library for building interactive competitive coding battles",
5
5
  "repository": "https://github.com/noamzaks/code-battles",
6
6
  "homepage": "https://code-battles.readthedocs.org",