Merge pull request #55 from djwesty/djwesty/40

Improve Chip Distribution (Issue #40)
This commit is contained in:
David Westgate 2025-03-09 17:55:22 -07:00 committed by GitHub
commit 292cd7b797
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 179 additions and 121 deletions

View File

@ -9,7 +9,8 @@
"userInterfaceStyle": "automatic", "userInterfaceStyle": "automatic",
"newArchEnabled": true, "newArchEnabled": true,
"ios": { "ios": {
"supportsTablet": true "supportsTablet": true,
"bundleIdentifier": "com.anonymous.pokerchipshelper"
}, },
"android": { "android": {
"adaptiveIcon": { "adaptiveIcon": {

View File

@ -10,6 +10,14 @@ interface BuyInSelectorProps {
} }
const defaultBuyInOptions = [10, 25, 50]; const defaultBuyInOptions = [10, 25, 50];
const MIN = 1;
const MAX = 200;
const parseRoundClamp = (num: string): number => {
const parsed = parseFloat(num);
const rounded = Math.round(parsed);
return Math.min(Math.max(rounded, MIN), MAX);
};
const BuyInSelector: React.FC<BuyInSelectorProps> = ({ const BuyInSelector: React.FC<BuyInSelectorProps> = ({
setBuyInAmount, setBuyInAmount,
@ -19,9 +27,9 @@ const BuyInSelector: React.FC<BuyInSelectorProps> = ({
const [buyInAmount, setBuyInAmountState] = useState<number | null>(null); const [buyInAmount, setBuyInAmountState] = useState<number | null>(null);
const handleCustomAmountChange = (value: string) => { const handleCustomAmountChange = (value: string) => {
const numericValue = parseFloat(value); const numericValue = parseRoundClamp(value);
if (!isNaN(numericValue) && numericValue >= 0) { if (!isNaN(numericValue) && numericValue >= 0) {
setCustomAmount(value); setCustomAmount(numericValue.toString());
setBuyInAmountState(numericValue); setBuyInAmountState(numericValue);
setBuyInAmount(numericValue); setBuyInAmount(numericValue);
} else { } else {
@ -50,18 +58,17 @@ const BuyInSelector: React.FC<BuyInSelectorProps> = ({
))} ))}
</View> </View>
<Text style={styles.p}>{i18n.t("custom_buy_in")}</Text>
<TextInput <TextInput
style={styles.input} style={styles.input}
value={customAmount} value={customAmount}
maxLength={3}
onChangeText={handleCustomAmountChange} onChangeText={handleCustomAmountChange}
placeholder={i18n.t("enter_custom_buy_in")} placeholder={`${i18n.t("custom_buy_in")} ${MIN} - ${MAX}`}
keyboardType="numeric" keyboardType="numeric"
/> />
<Text style={styles.h2}> <Text style={styles.h2}>
{`${i18n.t("selected_buy_in")}: `} {`${i18n.t("selected_buy_in")} `}
{buyInAmount !== null {buyInAmount !== null
? `${selectedCurrency} ${buyInAmount}` ? `${selectedCurrency} ${buyInAmount}`
: i18n.t("none")} : i18n.t("none")}

View File

@ -1,8 +1,9 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import { View, Text, StyleSheet } from "react-native"; import { View, Text, Alert } from "react-native";
import { ColorValue } from "react-native"; import { ColorValue } from "react-native";
import i18n from "@/i18n/i18n"; import i18n from "@/i18n/i18n";
import styles from "@/styles/styles"; import styles, { COLORS } from "@/styles/styles";
import { MaterialIcons } from "@expo/vector-icons";
interface ChipDistributionSummaryProps { interface ChipDistributionSummaryProps {
playerCount: number; playerCount: number;
@ -12,6 +13,8 @@ interface ChipDistributionSummaryProps {
selectedCurrency: string; selectedCurrency: string;
} }
const reverseFib: number[] = [8, 5, 3, 2, 1];
const ChipDistributionSummary = ({ const ChipDistributionSummary = ({
playerCount, playerCount,
buyInAmount, buyInAmount,
@ -20,11 +23,15 @@ const ChipDistributionSummary = ({
selectedCurrency = "$", selectedCurrency = "$",
}: ChipDistributionSummaryProps) => { }: ChipDistributionSummaryProps) => {
const validDenominations: validDenomination[] = [ const validDenominations: validDenomination[] = [
0.05, 0.1, 0.25, 0.5, 1, 2, 2.5, 5, 10, 20, 50, 100, 0.05, 0.1, 0.25, 1, 5, 10, 20, 50, 100,
]; ];
const [denominations, setDenominations] = useState<validDenomination[]>([]); const [denominations, setDenominations] = useState<validDenomination[]>([]);
const [distributions, setDistributions] = useState<number[]>([]); const [distributions, setDistributions] = useState<number[]>([]);
const showAlert = () => {
Alert.alert(i18n.t("warning"), i18n.t("chip_value_warn"));
};
type validDenomination = type validDenomination =
| 0.05 | 0.05
| 0.1 | 0.1
@ -36,120 +43,112 @@ const ChipDistributionSummary = ({
| 5 | 5
| 10 | 10
| 20 | 20
| 25
| 50 | 50
| 100; | 100;
const findFloorDenomination = (target: number): validDenomination => { const findFloorDenomination = (target: number): validDenomination => {
let current: validDenomination = validDenominations[0]; let current: validDenomination = validDenominations[0];
validDenominations.forEach((value, index) => { validDenominations.forEach((value, _) => {
if (value < target) current = value; if (value < target) current = value;
}); });
return current; return current;
}; };
const maxDenomination = useMemo(() => { const round = useCallback((num: number) => Math.round(num * 100) / 100, []);
if (totalChipsCount.length > 3) {
return findFloorDenomination(buyInAmount / 3); // Bound for the value of the highest chip
} else { // This is somewhat arbitray and imperfect, but 1/3 to 1/5 is reasonable depending on the number of colors.
return findFloorDenomination(buyInAmount / 4); // Could be possibly improved based on value of buy in amount
const maxDenomination: validDenomination = useMemo(() => {
let max: validDenomination;
switch (totalChipsCount.length) {
case 5:
case 4:
max = findFloorDenomination(buyInAmount / 3);
break;
case 3:
max = findFloorDenomination(buyInAmount / 4);
break;
case 2:
case 1:
default:
max = findFloorDenomination(buyInAmount / 5);
break;
} }
}, [totalChipsCount]); return max;
}, [totalChipsCount, buyInAmount]);
const potValue = useMemo( const potValue = useMemo(
() => buyInAmount * playerCount, () => buyInAmount * playerCount,
[buyInAmount, playerCount] [buyInAmount, playerCount]
); );
// The total value of all chips distributed to a single player. Ideally should be equal to buyInAmount
const totalValue = useMemo(() => { const totalValue = useMemo(() => {
let value = 0; let value = 0;
for (let i = 0; i < totalChipsCount.length; i++) { for (let i = 0; i < distributions.length; i++) {
value += distributions[i] * denominations[i]; value += distributions[i] * denominations[i];
} }
return value; return value;
}, [distributions, denominations]); }, [distributions, denominations]);
// Maximum quantity of each chip color which may be distributed to a single player before running out
const maxPossibleDistribution = useMemo( const maxPossibleDistribution = useMemo(
() => totalChipsCount.map((v) => Math.floor(v / playerCount)), () => totalChipsCount.map((v) => Math.floor(v / playerCount)),
[totalChipsCount, playerCount] [totalChipsCount, playerCount]
); );
const redenominate = useCallback( // Dynamically set denominations and distributions from changing inputs
(
invalidDenomination: validDenomination[],
shuffleIndex: number
): validDenomination[] => {
let moved = false;
const newDenomination: validDenomination[] = [];
for (let i = invalidDenomination.length - 1; i >= 0; i--) {
if (i > shuffleIndex) {
newDenomination.push(invalidDenomination[i]);
} else if (i == shuffleIndex) {
newDenomination.push(invalidDenomination[i]);
} else if (i < shuffleIndex && !moved) {
const nextLowestDenominationIndex = Math.max(
validDenominations.indexOf(invalidDenomination[i]) - 1,
0
);
newDenomination.push(validDenominations[nextLowestDenominationIndex]);
moved = true;
} else {
newDenomination.push(invalidDenomination[i]);
}
}
newDenomination.reverse();
return newDenomination;
},
[]
);
useEffect(() => { useEffect(() => {
let testDenomination: validDenomination[] = []; let testDenomination: validDenomination[] = [];
const numColors = totalChipsCount.length; const totalNumColors = totalChipsCount.length;
const testDistribution: number[] = [];
for (let i = 0; i < numColors; ++i) {
testDistribution.push(0);
}
// Start with max denominations, then push on the next adjacent lower denomination
testDenomination.push(maxDenomination); testDenomination.push(maxDenomination);
let currentDenominationIndex: number = let currentDenominationIndex: number =
validDenominations.indexOf(maxDenomination); validDenominations.indexOf(maxDenomination);
for (let i = numColors - 2; i >= 0; i = i - 1) { for (
let i = totalNumColors - 2;
i >= 0 && currentDenominationIndex > 0;
i = i - 1
) {
currentDenominationIndex -= 1; currentDenominationIndex -= 1;
const currentDemoniation = validDenominations[currentDenominationIndex]; const currentDemoniation = validDenominations[currentDenominationIndex];
testDenomination.push(currentDemoniation); testDenomination.push(currentDemoniation);
} }
testDenomination.reverse(); testDenomination.reverse();
let numColors = testDenomination.length;
let remainingValue = buyInAmount; const testDistribution: number[] = [];
let fail = true; for (let i = 0; i < numColors; ++i) {
let failCount = 0; testDistribution.push(0);
while (fail && failCount < 1) {
let stop = false;
while (remainingValue > 0 && !stop) {
let distributed = false;
for (let i = numColors - 1; i >= 0; i = i - 1) {
if (testDistribution[i] < maxPossibleDistribution[i]) {
if (remainingValue >= testDenomination[i]) {
testDistribution[i] = testDistribution[i] + 1;
remainingValue = remainingValue - testDenomination[i];
distributed = true;
}
}
}
if (distributed == false) {
stop = true;
}
}
if (remainingValue !== 0) {
const redenominateIndex = failCount % numColors;
testDenomination = redenominate(testDenomination, redenominateIndex);
failCount += 1;
fail = true;
} else {
fail = false;
}
} }
// Distribute the chips using the test denomination with a reverse fibbonaci preference
// Not optimal, nor correct under all inputs but works for most inputs
// Algorithm could be improved with more complexity and optimization (re-tries, redenominating, etc.)
let remainingValue = buyInAmount;
let stop = false;
while (remainingValue > 0 && !stop) {
let distributed = false;
for (let i = numColors - 1; i >= 0; i = i - 1) {
for (
let j = reverseFib[i];
j > 0 &&
remainingValue >= testDenomination[i] &&
testDistribution[i] < maxPossibleDistribution[i];
j = j - 1
) {
testDistribution[i] = testDistribution[i] + 1;
remainingValue = round(remainingValue - testDenomination[i]);
distributed = true;
}
}
if (distributed == false) {
stop = true;
}
}
setDenominations(testDenomination); setDenominations(testDenomination);
setDistributions(testDistribution); setDistributions(testDistribution);
}, [totalChipsCount, maxDenomination, buyInAmount, playerCount]); }, [totalChipsCount, maxDenomination, buyInAmount, playerCount]);
@ -157,24 +156,39 @@ const ChipDistributionSummary = ({
return ( return (
<> <>
<View style={styles.container}> <View style={styles.container}>
{totalChipsCount.map((_, index) => ( {distributions.map((distribution, index) => {
<View style={{ flexDirection: "row" }} key={index}> return (
<Text distribution > 0 && (
style={{ <View style={{ flexDirection: "row" }} key={index}>
...styles.h2, <Text
color: colors[index], style={{
...(colors[index] === "white" && styles.shadow), ...styles.h2,
}} fontWeight: "bold",
> color: colors[index],
{`${distributions[index]} ${i18n.t("chips")}: ${selectedCurrency}${denominations[index]} ${i18n.t("each")}`} ...(colors[index] === "white" && styles.shadow),
</Text> }}
</View> >
))} {`${distribution} ${i18n.t("chips")}: ${selectedCurrency}${denominations[index]} ${i18n.t("each")}`}
</Text>
</View>
)
);
})}
</View> </View>
<View style={{ flexDirection: "row", justifyContent: "space-between" }}> <View style={{ flexDirection: "row", justifyContent: "space-between" }}>
<Text style={styles.p}> <View style={[styles.container, { flexDirection: "row", gap: 1 }]}>
{i18n.t("total_value")}: {selectedCurrency} {totalValue} <Text style={styles.p}>
</Text> {i18n.t("total_value")}: {selectedCurrency} {round(totalValue)}{" "}
</Text>
{round(totalValue) !== buyInAmount && (
<MaterialIcons
name="warning"
size={20}
color={COLORS.WARNING}
onPress={showAlert}
/>
)}
</View>
<Text style={styles.p}> <Text style={styles.p}>
{selectedCurrency} {potValue} {i18n.t("pot")} {selectedCurrency} {potValue} {i18n.t("pot")}
</Text> </Text>

View File

@ -14,6 +14,7 @@ import styles from "@/styles/styles";
import i18n from "@/i18n/i18n"; import i18n from "@/i18n/i18n";
const colors: ColorValue[] = ["white", "red", "green", "blue", "black"]; const colors: ColorValue[] = ["white", "red", "green", "blue", "black"];
const defaults = [100, 50, 50, 50, 50];
const ChipInputModal = ({ const ChipInputModal = ({
showModal, showModal,
@ -152,7 +153,7 @@ const ChipsSelector = ({
} else if (numberOfChips > totalChipsCount.length) { } else if (numberOfChips > totalChipsCount.length) {
for (let colorIndex = 0; colorIndex < numberOfChips; ++colorIndex) { for (let colorIndex = 0; colorIndex < numberOfChips; ++colorIndex) {
if (colorIndex >= newTotalChipsCount.length) { if (colorIndex >= newTotalChipsCount.length) {
const defaultTotal = 100 - colorIndex * 20; const defaultTotal = defaults[colorIndex];
newTotalChipsCount.push(defaultTotal); newTotalChipsCount.push(defaultTotal);
} }
} }

View File

@ -39,7 +39,9 @@ describe("BuyInSelector Component", () => {
expect(getByText("$ 10")).toBeTruthy(); expect(getByText("$ 10")).toBeTruthy();
expect(getByText("$ 25")).toBeTruthy(); expect(getByText("$ 25")).toBeTruthy();
expect(getByText("$ 50")).toBeTruthy(); expect(getByText("$ 50")).toBeTruthy();
expect(getByPlaceholderText("Enter custom buy-in")).toBeTruthy(); expect(
getByPlaceholderText("Or, enter a custom amount: 1 - 200")
).toBeTruthy();
expect(queryByText(/Selected Buy-in:.*None/i)).toBeTruthy(); expect(queryByText(/Selected Buy-in:.*None/i)).toBeTruthy();
}); });
@ -53,24 +55,36 @@ describe("BuyInSelector Component", () => {
it("sets a custom buy-in amount correctly", () => { it("sets a custom buy-in amount correctly", () => {
const { getByPlaceholderText } = renderComponent(); const { getByPlaceholderText } = renderComponent();
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "100"); fireEvent.changeText(
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"100"
);
expect(setBuyInAmount).toHaveBeenCalledWith(100); expect(setBuyInAmount).toHaveBeenCalledWith(100);
}); });
it("resets custom amount if invalid input is entered", () => { it("bound and validate custom amount if invalid input is entered", () => {
const { getByPlaceholderText } = renderComponent(); const { getByPlaceholderText } = renderComponent();
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "-10"); fireEvent.changeText(
expect(setBuyInAmount).toHaveBeenCalledWith(25); // Default reset getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"-10"
);
expect(setBuyInAmount).toHaveBeenCalledWith(1); // Min value
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "abc"); fireEvent.changeText(
expect(setBuyInAmount).toHaveBeenCalledWith(25); getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"abc"
);
expect(setBuyInAmount).toHaveBeenCalledWith(1);
}); });
it("clears the custom amount when selecting a predefined option", () => { it("clears the custom amount when selecting a predefined option", () => {
const { getByPlaceholderText, getByText } = renderComponent(); const { getByPlaceholderText, getByText } = renderComponent();
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "100"); fireEvent.changeText(
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"100"
);
fireEvent.press(getByText("$ 50")); fireEvent.press(getByText("$ 50"));
expect(setBuyInAmount).toHaveBeenCalledWith(50); expect(setBuyInAmount).toHaveBeenCalledWith(50);
}); });
@ -78,13 +92,22 @@ describe("BuyInSelector Component", () => {
it("handles valid and invalid input for custom amount correctly", () => { it("handles valid and invalid input for custom amount correctly", () => {
const { getByPlaceholderText } = renderComponent(); const { getByPlaceholderText } = renderComponent();
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "75"); fireEvent.changeText(
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"75"
);
expect(setBuyInAmount).toHaveBeenCalledWith(75); expect(setBuyInAmount).toHaveBeenCalledWith(75);
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "-5"); fireEvent.changeText(
expect(setBuyInAmount).toHaveBeenCalledWith(25); getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"-5"
);
expect(setBuyInAmount).toHaveBeenCalledWith(1);
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "abc"); fireEvent.changeText(
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"abc"
);
expect(setBuyInAmount).toHaveBeenCalledWith(25); expect(setBuyInAmount).toHaveBeenCalledWith(25);
}); });
@ -98,7 +121,7 @@ describe("BuyInSelector Component", () => {
it("resets to default buy-in when custom input is cleared", () => { it("resets to default buy-in when custom input is cleared", () => {
const { getByPlaceholderText } = renderComponent(); const { getByPlaceholderText } = renderComponent();
const input = getByPlaceholderText("Enter custom buy-in"); const input = getByPlaceholderText("Or, enter a custom amount: 1 - 200");
fireEvent.changeText(input, "75"); fireEvent.changeText(input, "75");
expect(setBuyInAmount).toHaveBeenCalledWith(75); expect(setBuyInAmount).toHaveBeenCalledWith(75);
@ -110,7 +133,10 @@ describe("BuyInSelector Component", () => {
it("updates state correctly when selecting predefined buy-in after entering a custom amount", () => { it("updates state correctly when selecting predefined buy-in after entering a custom amount", () => {
const { getByPlaceholderText, getByText } = renderComponent(); const { getByPlaceholderText, getByText } = renderComponent();
fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "200"); fireEvent.changeText(
getByPlaceholderText("Or, enter a custom amount: 1 - 200"),
"200"
);
expect(setBuyInAmount).toHaveBeenCalledWith(200); expect(setBuyInAmount).toHaveBeenCalledWith(200);
fireEvent.press(getByText("$ 10")); fireEvent.press(getByText("$ 10"));

View File

@ -2,13 +2,20 @@ import React from "react";
import { render } from "@testing-library/react-native"; import { render } from "@testing-library/react-native";
import ChipDistributionSummary from "../ChipDistributionSummary"; import ChipDistributionSummary from "../ChipDistributionSummary";
jest.mock("@expo/vector-icons", () => {
const { Text } = require("react-native");
return {
MaterialIcons: () => <Text>TestIcon</Text>,
};
});
describe("ChipDistributionSummary Component", () => { describe("ChipDistributionSummary Component", () => {
test("renders correctly with valid data", () => { test("renders correctly with valid data", () => {
const playerCount = 4; const playerCount = 4;
const totalChipsCount = [100, 80, 60, 40, 20]; const totalChipsCount = [100, 80, 60, 40, 20];
const buyInAmount = 20; const buyInAmount = 20;
const expectedDistribution = [2, 2, 1, 2, 2]; const expectedDistribution = [16, 12, 8, 6, 2];
const expectedDenominations = [0.5, 1, 2, 2.5, 5]; const expectedDenominations = [0.05, 0.1, 0.25, 1, 5];
const { getByText } = render( const { getByText } = render(
<ChipDistributionSummary <ChipDistributionSummary

View File

@ -8,8 +8,7 @@
"inr": "INR (₹)", "inr": "INR (₹)",
"select_number_of_players": "Select the Number of Players:", "select_number_of_players": "Select the Number of Players:",
"select_buyin_amount": "Select Buy-in Amount:", "select_buyin_amount": "Select Buy-in Amount:",
"custom_buy_in": "Or enter a custom amount:", "custom_buy_in": "Or, enter a custom amount:",
"enter_custom_buy_in": "Enter custom buy-in",
"selected_buy_in": "Selected Buy-in:", "selected_buy_in": "Selected Buy-in:",
"none": "None", "none": "None",
"pick_an_image": "Pick an image", "pick_an_image": "Pick an image",
@ -37,6 +36,7 @@
"failed_to_save_state": "Failed to save state.", "failed_to_save_state": "Failed to save state.",
"state_loaded_from": "State loaded from", "state_loaded_from": "State loaded from",
"info": "Info", "info": "Info",
"warning": "Warning",
"no_saved_state_found": "No saved state found.", "no_saved_state_found": "No saved state found.",
"automatic_chip_detection": "Automatic Chip Detection", "automatic_chip_detection": "Automatic Chip Detection",
"manual_chip_adjustment": "Manual Chip Adjustment", "manual_chip_adjustment": "Manual Chip Adjustment",
@ -45,6 +45,7 @@
"save_slot_2": "Save\nSlot 2", "save_slot_2": "Save\nSlot 2",
"load_slot_1": "Load\nSlot 1", "load_slot_1": "Load\nSlot 1",
"load_slot_2": "Load\nSlot 2", "load_slot_2": "Load\nSlot 2",
"please_select_valid_buyin": "Please select a valid buy-in amount" "please_select_valid_buyin": "Please select a valid buy-in amount",
"chip_value_warn": "Be advised that the value of the distributed chips does not equal the buy-in for these inputs.\n\nHowever, results shown are fair to all players"
} }
} }

View File

@ -8,8 +8,7 @@
"inr": "INR (₹)", "inr": "INR (₹)",
"select_number_of_players": "Seleccionar número de jugadores:", "select_number_of_players": "Seleccionar número de jugadores:",
"select_buyin_amount": "Seleccionar cantidad de buy-in:", "select_buyin_amount": "Seleccionar cantidad de buy-in:",
"custom_buy_in": "O ingresa una cantidad personalizada:", "custom_buy_in": "O, ingresa una cantidad personalizada:",
"enter_custom_buy_in": "Ingresar buy-in personalizado",
"selected_buy_in": "Buy-in seleccionado:", "selected_buy_in": "Buy-in seleccionado:",
"none": "Ninguno", "none": "Ninguno",
"pick_an_image": "Elige una imagen", "pick_an_image": "Elige una imagen",
@ -38,6 +37,7 @@
"failed_to_save_state": "No se pudo guardar el estado.", "failed_to_save_state": "No se pudo guardar el estado.",
"state_loaded_from": "Estado cargado desde", "state_loaded_from": "Estado cargado desde",
"info": "Información", "info": "Información",
"warning": "Advertencia",
"no_saved_state_found": "No se encontró estado guardado.", "no_saved_state_found": "No se encontró estado guardado.",
"automatic_chip_detection": "Detección automática de fichas", "automatic_chip_detection": "Detección automática de fichas",
"manual_chip_adjustment": "Ajuste manual de fichas", "manual_chip_adjustment": "Ajuste manual de fichas",
@ -46,6 +46,7 @@
"save_slot_2": "Guardar\nSlot 2", "save_slot_2": "Guardar\nSlot 2",
"load_slot_1": "Cargar\nSlot 1", "load_slot_1": "Cargar\nSlot 1",
"load_slot_2": "Cargar\nSlot 2", "load_slot_2": "Cargar\nSlot 2",
"please_select_valid_buyin": "Por favor seleccione una cantidad de buy-in válida" "please_select_valid_buyin": "Por favor seleccione una cantidad de buy-in válida",
"chip_value_warn": "Tenga en cuenta que el valor de las fichas distribuidas no es igual al buy-in para estas entradas.\n\nSin embargo, los resultados que se muestran son justos para todos los jugadores."
} }
} }

View File

@ -5,7 +5,7 @@ export const COLORS = {
SECONDARY: "#6c757d", SECONDARY: "#6c757d",
SUCCESS: "#28a745", SUCCESS: "#28a745",
DANGER: "#dc3545", DANGER: "#dc3545",
WARNING: "#ffc107", WARNING: "#c79c28",
}; };
const lightStyles = StyleSheet.create({}); const lightStyles = StyleSheet.create({});