From d9a835c31506f0e65c0a8beb315f9d9f9c2a9bd2 Mon Sep 17 00:00:00 2001 From: vutukuri15 Date: Sat, 15 Feb 2025 19:56:41 -0800 Subject: [PATCH 1/9] Displaying Chip value contribution --- components/ChipDistributionSummary.tsx | 140 ++++++++++++++++++++----- 1 file changed, 116 insertions(+), 24 deletions(-) diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index 19fd70d..4b78b5d 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useEffect, useState } from "react"; import { View, Text, StyleSheet } from "react-native"; import { ColorValue } from "react-native"; @@ -9,8 +9,6 @@ interface ChipDistributionSummaryProps { colors?: ColorValue[]; } -const MAX_CHIPS = 500; - const ChipDistributionSummary = ({ playerCount, buyInAmount, @@ -20,41 +18,135 @@ const ChipDistributionSummary = ({ const [chipDistribution, setChipDistribution] = useState([]); useEffect(() => { - if (buyInAmount !== null && playerCount > 0) { - let totalChips = totalChipsCount.reduce((sum, count) => sum + count, 0); - - if (totalChips > MAX_CHIPS) { - const scaleFactor = MAX_CHIPS / totalChips; - totalChipsCount = totalChipsCount.map((count) => Math.floor(count * scaleFactor)); - totalChips = MAX_CHIPS; + if ( + buyInAmount !== null && + playerCount > 0 && + totalChipsCount.every((chips) => chips > 0) + ) { + const chipValues = [0.05, 0.25, 1, 2.5, 5]; // Chip values: white, red, green, blue, black + let distribution = [0, 0, 0, 0, 0]; // Number of chips per player for each color + let remainingValue = buyInAmount; + let remainingChips = [...totalChipsCount]; // Make a copy of the available chips + + console.log("Starting distribution with buy-in amount:", buyInAmount); + console.log("Player count:", playerCount); + console.log("Remaining value to distribute:", remainingValue); + console.log("Available chips:", remainingChips); + + // Step 1: Distribute chips from highest to lowest denomination + for (let i = chipValues.length - 1; i >= 0; i--) { + const chipValue = chipValues[i]; + const availableChips = remainingChips[i]; + + console.log(`Attempting to distribute ${chipValue} chips`); + + // Calculate how many chips we can distribute to each player + const maxChipsPerPlayer = Math.min( + Math.floor(remainingValue / chipValue), // Max chips based on remaining value + Math.floor(availableChips / playerCount) // Max chips based on availability + ); + + console.log( + `Max chips per player for ${chipValue} value: ${maxChipsPerPlayer}` + ); + + if (maxChipsPerPlayer > 0) { + // Distribute the chips + const chipsToDistribute = Math.min( + maxChipsPerPlayer, + Math.floor(remainingValue / chipValue) + ); + distribution[i] = chipsToDistribute; + + remainingValue -= chipsToDistribute * chipValue; // Subtract the value of these chips + remainingChips[i] -= chipsToDistribute * playerCount; // Update remaining chips + + console.log( + `Distributed ${chipsToDistribute} chips of ${chipValue} value` + ); + } + + if (remainingValue <= 0) break; // Stop once the required value is met } - const distribution = totalChipsCount.map((chipCount) => - Math.floor(chipCount / playerCount) + + console.log("Remaining value after distribution:", remainingValue); + console.log("Remaining chips:", remainingChips); + + // Step 2: Handle the remaining value with smaller denominations if necessary + if (remainingValue > 0) { + for (let i = 0; i < chipValues.length; i++) { + const chipValue = chipValues[i]; + const availableChips = remainingChips[i]; + + const maxChipsPerPlayer = Math.min( + Math.floor(remainingValue / chipValue), // Max chips based on remaining value + Math.floor(availableChips / playerCount) // Max chips based on availability + ); + + console.log( + `Attempting to distribute ${chipValue} chips (remaining value: ${remainingValue})` + ); + + if (maxChipsPerPlayer > 0) { + const chipsToDistribute = Math.min( + maxChipsPerPlayer, + Math.floor(remainingValue / chipValue) + ); + distribution[i] += chipsToDistribute; + + remainingValue -= chipsToDistribute * chipValue; // Subtract the value of these chips + remainingChips[i] -= chipsToDistribute * playerCount; // Update remaining chips + + console.log( + `Distributed ${chipsToDistribute} chips of ${chipValue} value` + ); + } + + if (remainingValue <= 0) break; // Stop if the remaining value is fulfilled + } + } + + console.log("Remaining value after distribution:", remainingValue); + console.log("Final chip distribution:", distribution); + + // Step 3: Adjust distribution to ensure no player gets more chips than available + distribution = distribution.map((chipCount, i) => + Math.min(chipCount, Math.floor(totalChipsCount[i] / playerCount)) ); - + + // Step 4: Check if total value distributed per player matches the buy-in + const totalDistributedValue = distribution.reduce( + (total, chips, index) => total + chips * chipValues[index], + 0 + ); + + console.log("Total distributed value:", totalDistributedValue); + + // If total distributed value doesn't match, reset distribution + if (totalDistributedValue !== buyInAmount) { + console.log("Mismatch in distributed value, resetting distribution."); + distribution = [0, 0, 0, 0, 0]; + } + setChipDistribution(distribution); } else { setChipDistribution([]); } }, [buyInAmount, playerCount, totalChipsCount]); - const hasValidDistribution = useMemo( - () => - buyInAmount !== null && playerCount > 0 && chipDistribution.length > 0, - [buyInAmount, playerCount, chipDistribution] - ); - return ( Chip Distribution Summary: - {hasValidDistribution ? ( + {chipDistribution.length > 0 ? ( chipDistribution.map((count, index) => ( - - {`${colors[index]?.toString().toUpperCase()} Chips: ${count} per player`} + + {`${colors[index]?.toString().toUpperCase()} Chips: ${count} per player`} )) ) : ( - No valid distribution calculated yet. + + No valid distribution calculated yet. + )} ); @@ -82,4 +174,4 @@ const styles = StyleSheet.create({ }, }); -export default ChipDistributionSummary; \ No newline at end of file +export default ChipDistributionSummary; -- 2.39.5 From b9c90d24934a9262a4f0c7fd1b8e05cb86a54016 Mon Sep 17 00:00:00 2001 From: vutukuri15 Date: Mon, 17 Feb 2025 22:54:05 -0800 Subject: [PATCH 2/9] Modified code --- app/index.tsx | 2 +- components/ChipDistributionSummary.tsx | 177 +++++++++++-------------- 2 files changed, 75 insertions(+), 104 deletions(-) diff --git a/app/index.tsx b/app/index.tsx index eb5f1b3..f1a989e 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -3,7 +3,7 @@ import { ScrollView, Text, Alert, Button } from "react-native"; import PlayerSelector from "@/components/PlayerSelector"; import BuyInSelector from "@/components/BuyInSelector"; import ChipsSelector from "@/components/ChipsSelector"; -import ChipDistributionSummary from "@/components/ChipDistributionSummary"; +import ChipDistributionSummary from "@/components/ChipDistributionSummary"; const IndexScreen = () => { const [playerCount, setPlayerCount] = useState(2); const [buyInAmount, setBuyInAmount] = useState(null); diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index 4b78b5d..1db9340 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -15,7 +15,9 @@ const ChipDistributionSummary = ({ totalChipsCount, colors = ["white", "red", "green", "blue", "black"], }: ChipDistributionSummaryProps) => { - const [chipDistribution, setChipDistribution] = useState([]); + const [chipCountPerPlayer, setChipCountPerPlayer] = useState< + Record + >({}); useEffect(() => { if ( @@ -23,126 +25,92 @@ const ChipDistributionSummary = ({ playerCount > 0 && totalChipsCount.every((chips) => chips > 0) ) { - const chipValues = [0.05, 0.25, 1, 2.5, 5]; // Chip values: white, red, green, blue, black - let distribution = [0, 0, 0, 0, 0]; // Number of chips per player for each color + const validDenominations = [ + 0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 20, 50, 100, + ]; + + const availableColors = Math.min(colors.length, 5); + let selectedChips: number[] = []; + let maxDenomination = buyInAmount / 2; + + // Select the denominations for available colors (up to 5 colors) + for (let i = validDenominations.length - 1; i >= 0; i--) { + if ( + validDenominations[i] <= maxDenomination && + selectedChips.length < availableColors + ) { + selectedChips.unshift(validDenominations[i]); + } + } + + // Ensure the selected chips are sorted from low to high denomination + selectedChips = selectedChips.sort((a, b) => a - b); + + let distribution = new Array(selectedChips.length).fill(0); let remainingValue = buyInAmount; - let remainingChips = [...totalChipsCount]; // Make a copy of the available chips + let remainingChips = [...totalChipsCount.slice(0, selectedChips.length)]; - console.log("Starting distribution with buy-in amount:", buyInAmount); - console.log("Player count:", playerCount); - console.log("Remaining value to distribute:", remainingValue); - console.log("Available chips:", remainingChips); - - // Step 1: Distribute chips from highest to lowest denomination - for (let i = chipValues.length - 1; i >= 0; i--) { - const chipValue = chipValues[i]; - const availableChips = remainingChips[i]; - - console.log(`Attempting to distribute ${chipValue} chips`); - - // Calculate how many chips we can distribute to each player - const maxChipsPerPlayer = Math.min( - Math.floor(remainingValue / chipValue), // Max chips based on remaining value - Math.floor(availableChips / playerCount) // Max chips based on availability - ); - - console.log( - `Max chips per player for ${chipValue} value: ${maxChipsPerPlayer}` - ); - - if (maxChipsPerPlayer > 0) { - // Distribute the chips - const chipsToDistribute = Math.min( - maxChipsPerPlayer, - Math.floor(remainingValue / chipValue) - ); - distribution[i] = chipsToDistribute; - - remainingValue -= chipsToDistribute * chipValue; // Subtract the value of these chips - remainingChips[i] -= chipsToDistribute * playerCount; // Update remaining chips - - console.log( - `Distributed ${chipsToDistribute} chips of ${chipValue} value` - ); + // First pass: Distribute at least one chip of each selected denomination per player + for (let i = 0; i < selectedChips.length; i++) { + const chipValue = selectedChips[i]; + if (remainingValue >= chipValue && remainingChips[i] >= playerCount) { + distribution[i] = 1; + remainingValue -= chipValue; + remainingChips[i] -= playerCount; } - - if (remainingValue <= 0) break; // Stop once the required value is met } - console.log("Remaining value after distribution:", remainingValue); - console.log("Remaining chips:", remainingChips); + // Second pass: Distribute remaining buy-in amount fairly across chip colors + while (remainingValue > 0) { + let allocatedInRound = false; - // Step 2: Handle the remaining value with smaller denominations if necessary - if (remainingValue > 0) { - for (let i = 0; i < chipValues.length; i++) { - const chipValue = chipValues[i]; - const availableChips = remainingChips[i]; + for (let i = 0; i < selectedChips.length; i++) { + if (remainingValue <= 0) break; + const chipValue = selectedChips[i]; - const maxChipsPerPlayer = Math.min( - Math.floor(remainingValue / chipValue), // Max chips based on remaining value - Math.floor(availableChips / playerCount) // Max chips based on availability - ); - - console.log( - `Attempting to distribute ${chipValue} chips (remaining value: ${remainingValue})` - ); - - if (maxChipsPerPlayer > 0) { - const chipsToDistribute = Math.min( - maxChipsPerPlayer, - Math.floor(remainingValue / chipValue) - ); - distribution[i] += chipsToDistribute; - - remainingValue -= chipsToDistribute * chipValue; // Subtract the value of these chips - remainingChips[i] -= chipsToDistribute * playerCount; // Update remaining chips - - console.log( - `Distributed ${chipsToDistribute} chips of ${chipValue} value` - ); + if (remainingChips[i] >= playerCount && remainingValue >= chipValue) { + distribution[i] += 1; + remainingValue -= chipValue; + remainingChips[i] -= playerCount; + allocatedInRound = true; } + } - if (remainingValue <= 0) break; // Stop if the remaining value is fulfilled + if (!allocatedInRound) break; // Prevent infinite loops + } + + // Create a mapping from chip color names to chip counts and denominations + let chipMap: Record = {}; + for (let i = 0; i < selectedChips.length; i++) { + if (distribution[i] > 0) { + // Map denomination and color to chip count + chipMap[colors[i]] = { + count: distribution[i], + value: selectedChips[i], + }; } } - console.log("Remaining value after distribution:", remainingValue); - console.log("Final chip distribution:", distribution); - - // Step 3: Adjust distribution to ensure no player gets more chips than available - distribution = distribution.map((chipCount, i) => - Math.min(chipCount, Math.floor(totalChipsCount[i] / playerCount)) - ); - - // Step 4: Check if total value distributed per player matches the buy-in - const totalDistributedValue = distribution.reduce( - (total, chips, index) => total + chips * chipValues[index], - 0 - ); - - console.log("Total distributed value:", totalDistributedValue); - - // If total distributed value doesn't match, reset distribution - if (totalDistributedValue !== buyInAmount) { - console.log("Mismatch in distributed value, resetting distribution."); - distribution = [0, 0, 0, 0, 0]; - } - - setChipDistribution(distribution); + setChipCountPerPlayer(chipMap); } else { - setChipDistribution([]); + setChipCountPerPlayer({}); } - }, [buyInAmount, playerCount, totalChipsCount]); + }, [buyInAmount, playerCount, totalChipsCount, colors]); return ( Chip Distribution Summary: - {chipDistribution.length > 0 ? ( - chipDistribution.map((count, index) => ( - - {`${colors[index]?.toString().toUpperCase()} Chips: ${count} per player`} - - )) + {Object.keys(chipCountPerPlayer).length > 0 ? ( + + {Object.entries(chipCountPerPlayer).map( + ([color, { count, value }]) => ( + + {color.charAt(0).toUpperCase() + color.slice(1)} chips: {count}{" "} + ( ${value} each) + + ) + )} + ) : ( No valid distribution calculated yet. @@ -164,6 +132,9 @@ const styles = StyleSheet.create({ fontWeight: "bold", marginBottom: 10, }, + chipContainer: { + marginTop: 10, + }, chipText: { fontSize: 16, marginVertical: 2, -- 2.39.5 From f2710588ea9458078d02b4e186ac17851294bbea Mon Sep 17 00:00:00 2001 From: David Westgate Date: Thu, 20 Feb 2025 14:18:56 -0800 Subject: [PATCH 3/9] start rework of algorithm --- app/index.tsx | 2 +- components/BuyInSelector.tsx | 6 +- components/ChipDistributionSummary.tsx | 178 +++++++++++++++---------- 3 files changed, 110 insertions(+), 76 deletions(-) diff --git a/app/index.tsx b/app/index.tsx index f1a989e..df6c883 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -6,7 +6,7 @@ import ChipsSelector from "@/components/ChipsSelector"; import ChipDistributionSummary from "@/components/ChipDistributionSummary"; const IndexScreen = () => { const [playerCount, setPlayerCount] = useState(2); - const [buyInAmount, setBuyInAmount] = useState(null); + const [buyInAmount, setBuyInAmount] = useState(20); const [numberOfChips, setNumberOfChips] = useState(5); const [totalChipsCount, setTotalChipsCount] = useState([]); const handleSave = () => { diff --git a/components/BuyInSelector.tsx b/components/BuyInSelector.tsx index 48d56c1..c986d32 100644 --- a/components/BuyInSelector.tsx +++ b/components/BuyInSelector.tsx @@ -9,7 +9,7 @@ import { import { MaterialIcons } from "@expo/vector-icons"; interface BuyInSelectorProps { - setBuyInAmount: React.Dispatch>; + setBuyInAmount: React.Dispatch>; } const defaultBuyInOptions = [10, 25, 50]; @@ -26,8 +26,8 @@ const BuyInSelector: React.FC = ({ setBuyInAmount }) => { setBuyInAmount(numericValue); } else { setCustomAmount(""); - setBuyInAmountState(null); - setBuyInAmount(null); + setBuyInAmountState(25); + setBuyInAmount(25); } }; diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index 1db9340..76a67e4 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -1,10 +1,10 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { View, Text, StyleSheet } from "react-native"; import { ColorValue } from "react-native"; interface ChipDistributionSummaryProps { playerCount: number; - buyInAmount: number | null; + buyInAmount: number; totalChipsCount: number[]; colors?: ColorValue[]; } @@ -15,92 +15,126 @@ const ChipDistributionSummary = ({ totalChipsCount, colors = ["white", "red", "green", "blue", "black"], }: ChipDistributionSummaryProps) => { - const [chipCountPerPlayer, setChipCountPerPlayer] = useState< - Record - >({}); + // const [chipCountPerPlayer, setChipCountPerPlayer] = useState< + // Record + // >({}); + const validDenominations = [ + 0.05, 0.1, 0.25, 0.5, 1, 2, 2.5, 5, 10, 20, 50, 100, + ]; + + // Re-organize the inputs in a map for convience + const chipMap: Map = useMemo(() => { + const m: Map = new Map(); + totalChipsCount.map((v, i) => { + m.set(colors[i], v); + }); + return m; + }, [playerCount, buyInAmount, totalChipsCount]); + + // Helper function to return the closest (but lower) valid denomination to the target + const findFloorDenomination = (target: number) => { + let current: number = validDenominations[0]; + validDenominations.forEach((value, index) => { + if (value < target) current = value; + }); + return current; + }; + + const maxDenomination = useMemo(() => { + if (chipMap.size > 3) { + return findFloorDenomination(buyInAmount / 2); + } else { + return findFloorDenomination(buyInAmount / 4); + } + }, [chipMap]); useEffect(() => { - if ( - buyInAmount !== null && - playerCount > 0 && - totalChipsCount.every((chips) => chips > 0) - ) { - const validDenominations = [ - 0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 20, 50, 100, - ]; + const testDistribution: Map = new Map(); + const numColors = chipMap.size; + testDistribution.set(colors[numColors - 1], maxDenomination); + const maxDenominationIndex: number = + validDenominations.indexOf(maxDenomination); + console.log("test distribution", testDistribution); + }, [chipMap, maxDenomination]); - const availableColors = Math.min(colors.length, 5); - let selectedChips: number[] = []; - let maxDenomination = buyInAmount / 2; + // useEffect(() => { + // if ( + // buyInAmount !== null && + // playerCount > 0 && + // totalChipsCount.every((chips) => chips > 0) + // ) { + // const availableColors = Math.min(colors.length, 5); + // let selectedChips: number[] = []; + // let maxDenomination = buyInAmount / 2; - // Select the denominations for available colors (up to 5 colors) - for (let i = validDenominations.length - 1; i >= 0; i--) { - if ( - validDenominations[i] <= maxDenomination && - selectedChips.length < availableColors - ) { - selectedChips.unshift(validDenominations[i]); - } - } + // // Select the denominations for available colors (up to 5 colors) + // for (let i = validDenominations.length - 1; i >= 0; i--) { + // if ( + // validDenominations[i] <= maxDenomination && + // selectedChips.length < availableColors + // ) { + // selectedChips.unshift(validDenominations[i]); + // } + // } - // Ensure the selected chips are sorted from low to high denomination - selectedChips = selectedChips.sort((a, b) => a - b); + // // Ensure the selected chips are sorted from low to high denomination + // selectedChips = selectedChips.sort((a, b) => a - b); - let distribution = new Array(selectedChips.length).fill(0); - let remainingValue = buyInAmount; - let remainingChips = [...totalChipsCount.slice(0, selectedChips.length)]; + // let distribution = new Array(selectedChips.length).fill(0); + // let remainingValue = buyInAmount; + // let remainingChips = [...totalChipsCount.slice(0, selectedChips.length)]; - // First pass: Distribute at least one chip of each selected denomination per player - for (let i = 0; i < selectedChips.length; i++) { - const chipValue = selectedChips[i]; - if (remainingValue >= chipValue && remainingChips[i] >= playerCount) { - distribution[i] = 1; - remainingValue -= chipValue; - remainingChips[i] -= playerCount; - } - } + // // First pass: Distribute at least one chip of each selected denomination per player + // for (let i = 0; i < selectedChips.length; i++) { + // const chipValue = selectedChips[i]; + // if (remainingValue >= chipValue && remainingChips[i] >= playerCount) { + // distribution[i] = 1; + // remainingValue -= chipValue; + // remainingChips[i] -= playerCount; + // } + // } - // Second pass: Distribute remaining buy-in amount fairly across chip colors - while (remainingValue > 0) { - let allocatedInRound = false; + // // Second pass: Distribute remaining buy-in amount fairly across chip colors + // while (remainingValue > 0) { + // let allocatedInRound = false; - for (let i = 0; i < selectedChips.length; i++) { - if (remainingValue <= 0) break; - const chipValue = selectedChips[i]; + // for (let i = 0; i < selectedChips.length; i++) { + // if (remainingValue <= 0) break; + // const chipValue = selectedChips[i]; - if (remainingChips[i] >= playerCount && remainingValue >= chipValue) { - distribution[i] += 1; - remainingValue -= chipValue; - remainingChips[i] -= playerCount; - allocatedInRound = true; - } - } + // if (remainingChips[i] >= playerCount && remainingValue >= chipValue) { + // distribution[i] += 1; + // remainingValue -= chipValue; + // remainingChips[i] -= playerCount; + // allocatedInRound = true; + // } + // } - if (!allocatedInRound) break; // Prevent infinite loops - } + // if (!allocatedInRound) break; // Prevent infinite loops + // } - // Create a mapping from chip color names to chip counts and denominations - let chipMap: Record = {}; - for (let i = 0; i < selectedChips.length; i++) { - if (distribution[i] > 0) { - // Map denomination and color to chip count - chipMap[colors[i]] = { - count: distribution[i], - value: selectedChips[i], - }; - } - } + // // Create a mapping from chip color names to chip counts and denominations + // let chipMap: Record = {}; + // for (let i = 0; i < selectedChips.length; i++) { + // if (distribution[i] > 0) { + // // Map denomination and color to chip count + // chipMap[colors[i]] = { + // count: distribution[i], + // value: selectedChips[i], + // }; + // } + // } - setChipCountPerPlayer(chipMap); - } else { - setChipCountPerPlayer({}); - } - }, [buyInAmount, playerCount, totalChipsCount, colors]); + // setChipCountPerPlayer(chipMap); + // } else { + // setChipCountPerPlayer({}); + // } + // }, [buyInAmount, playerCount, totalChipsCount, colors]); return ( Chip Distribution Summary: - {Object.keys(chipCountPerPlayer).length > 0 ? ( + {/**Object.keys(chipCountPerPlayer).length > 0 ? ( {Object.entries(chipCountPerPlayer).map( ([color, { count, value }]) => ( @@ -115,7 +149,7 @@ const ChipDistributionSummary = ({ No valid distribution calculated yet. - )} + )**/} ); }; -- 2.39.5 From 1a027e1feb4c18f8ac44fd21dacb65818de6ee23 Mon Sep 17 00:00:00 2001 From: David Westgate Date: Fri, 21 Feb 2025 18:17:07 -0800 Subject: [PATCH 4/9] more work in progress on algorithm --- app/index.tsx | 9 ++ components/ChipDistributionSummary.tsx | 213 ++++++++++++++----------- 2 files changed, 126 insertions(+), 96 deletions(-) diff --git a/app/index.tsx b/app/index.tsx index df6c883..faf3557 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -4,6 +4,15 @@ import PlayerSelector from "@/components/PlayerSelector"; import BuyInSelector from "@/components/BuyInSelector"; import ChipsSelector from "@/components/ChipsSelector"; import ChipDistributionSummary from "@/components/ChipDistributionSummary"; + +export enum COLORS { + "white", + "red", + "green", + "blue", + "black", +} + const IndexScreen = () => { const [playerCount, setPlayerCount] = useState(2); const [buyInAmount, setBuyInAmount] = useState(20); diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index 76a67e4..00858ba 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from "react"; import { View, Text, StyleSheet } from "react-native"; import { ColorValue } from "react-native"; +// import { COLORS } from "@/app"; interface ChipDistributionSummaryProps { playerCount: number; @@ -15,12 +16,25 @@ const ChipDistributionSummary = ({ totalChipsCount, colors = ["white", "red", "green", "blue", "black"], }: ChipDistributionSummaryProps) => { - // const [chipCountPerPlayer, setChipCountPerPlayer] = useState< - // Record - // >({}); - const validDenominations = [ + const validDenominations: validDenomination[] = [ 0.05, 0.1, 0.25, 0.5, 1, 2, 2.5, 5, 10, 20, 50, 100, ]; + const [denominations, setDenominations] = useState([]); + const [distributions, setDistributions] = useState([]); + + type validDenomination = + | 0.05 + | 0.1 + | 0.25 + | 0.5 + | 1 + | 2 + | 2.5 + | 5 + | 10 + | 20 + | 50 + | 100; // Re-organize the inputs in a map for convience const chipMap: Map = useMemo(() => { @@ -32,124 +46,131 @@ const ChipDistributionSummary = ({ }, [playerCount, buyInAmount, totalChipsCount]); // Helper function to return the closest (but lower) valid denomination to the target - const findFloorDenomination = (target: number) => { - let current: number = validDenominations[0]; + const findFloorDenomination = (target: number): validDenomination => { + let current: validDenomination = validDenominations[0]; validDenominations.forEach((value, index) => { if (value < target) current = value; }); return current; }; + // Bound for the value of the highest chip const maxDenomination = useMemo(() => { if (chipMap.size > 3) { - return findFloorDenomination(buyInAmount / 2); + return findFloorDenomination(buyInAmount / 3); } else { return findFloorDenomination(buyInAmount / 4); } }, [chipMap]); + // Total value of the pot + const totalValue = useMemo( + () => buyInAmount * playerCount, + [buyInAmount, playerCount] + ); + + // Maximum quantity of each chip which may be distributed to a single player before running out + const maxPossibleDistribution = useMemo( + () => totalChipsCount.map((v) => Math.floor(v / playerCount)), + [totalChipsCount, playerCount] + ); + + function denominate(min: number, max: number, count: number = 4): number[] { + if (max - min + 1 < count) { + throw new Error( + "Range is too small to generate the required number of unique values." + ); + } + + const numbers = new Set(); + + while (numbers.size < count) { + const randomNum = Math.floor(Math.random() * (max - min + 1)) + min; + numbers.add(randomNum); + } + + // const denominations + + return Array.from(numbers); + } + useEffect(() => { - const testDistribution: Map = new Map(); + // const testDistribution: Map = new Map(); + const testDenomination: validDenomination[] = []; + const numColors = chipMap.size; - testDistribution.set(colors[numColors - 1], maxDenomination); - const maxDenominationIndex: number = + const testDistribution: number[] = []; + for (let i = 0; i < numColors; ++i) { + testDistribution.push(0); + } + + let numColorsRemaining = numColors; + // testDistribution.set(colors[numColors - 1], maxDenomination); + testDenomination.push(maxDenomination); + numColorsRemaining -= 1; + let currentDenominationIndex: number = validDenominations.indexOf(maxDenomination); - console.log("test distribution", testDistribution); - }, [chipMap, maxDenomination]); + while (numColorsRemaining > 0) { + numColorsRemaining -= 1; + currentDenominationIndex -= 1; + const currentDemoniation = validDenominations[currentDenominationIndex]; + testDenomination.push(currentDemoniation); + } + testDenomination.reverse(); - // useEffect(() => { - // if ( - // buyInAmount !== null && - // playerCount > 0 && - // totalChipsCount.every((chips) => chips > 0) - // ) { - // const availableColors = Math.min(colors.length, 5); - // let selectedChips: number[] = []; - // let maxDenomination = buyInAmount / 2; + console.log("BUY IN: ", buyInAmount); + console.log("PLAYER COUNT ", playerCount); - // // Select the denominations for available colors (up to 5 colors) - // for (let i = validDenominations.length - 1; i >= 0; i--) { - // if ( - // validDenominations[i] <= maxDenomination && - // selectedChips.length < availableColors - // ) { - // selectedChips.unshift(validDenominations[i]); - // } - // } + // DISTRIBUTE + let remainingValue = buyInAmount; + const remainingChips = [...maxPossibleDistribution]; + console.log("\ntest Denomination", testDenomination); + console.log("test distribution ", testDistribution); + console.log("remainingChips", remainingChips); + console.log("remaining value ", remainingValue); - // // Ensure the selected chips are sorted from low to high denomination - // selectedChips = selectedChips.sort((a, b) => a - b); + //First distribute one of each chip to each player + for (let i = numColors - 1; i >= 0; i--) { + testDistribution[i] = testDistribution[i] + 1; + remainingChips[i] = remainingChips[i] - 1; + remainingValue = remainingValue - testDenomination[i]; + } + console.log("\ntest Denomination", testDenomination); + console.log("test distribution ", testDistribution); + console.log("remainingChips", remainingChips); + console.log("remaining value ", remainingValue); - // let distribution = new Array(selectedChips.length).fill(0); - // let remainingValue = buyInAmount; - // let remainingChips = [...totalChipsCount.slice(0, selectedChips.length)]; + //Then, greedy approach to distribute remaining chips - // // First pass: Distribute at least one chip of each selected denomination per player - // for (let i = 0; i < selectedChips.length; i++) { - // const chipValue = selectedChips[i]; - // if (remainingValue >= chipValue && remainingChips[i] >= playerCount) { - // distribution[i] = 1; - // remainingValue -= chipValue; - // remainingChips[i] -= playerCount; - // } - // } - - // // Second pass: Distribute remaining buy-in amount fairly across chip colors - // while (remainingValue > 0) { - // let allocatedInRound = false; - - // for (let i = 0; i < selectedChips.length; i++) { - // if (remainingValue <= 0) break; - // const chipValue = selectedChips[i]; - - // if (remainingChips[i] >= playerCount && remainingValue >= chipValue) { - // distribution[i] += 1; - // remainingValue -= chipValue; - // remainingChips[i] -= playerCount; - // allocatedInRound = true; - // } - // } - - // if (!allocatedInRound) break; // Prevent infinite loops - // } - - // // Create a mapping from chip color names to chip counts and denominations - // let chipMap: Record = {}; - // for (let i = 0; i < selectedChips.length; i++) { - // if (distribution[i] > 0) { - // // Map denomination and color to chip count - // chipMap[colors[i]] = { - // count: distribution[i], - // value: selectedChips[i], - // }; - // } - // } - - // setChipCountPerPlayer(chipMap); - // } else { - // setChipCountPerPlayer({}); - // } - // }, [buyInAmount, playerCount, totalChipsCount, colors]); + // while (remainingValue > 0) { + // for (let i = numColors - 1; i >= 0; i--) { + // if (remainingChips[i] > 0 && remainingValue > testDenomination[i]) { + // testDistribution[i] = testDistribution[i] + 1; + // remainingChips[i] = remainingChips[i] - 1; + // remainingValue = remainingValue - testDenomination[i]; + // } + // remainingValue = 0; + // } + // } + console.log("\ntest Denomination", testDenomination); + console.log("test distribution ", testDistribution); + console.log("remainingChips", remainingChips); + console.log("remaining value ", remainingValue); + setDenominations(testDenomination); + setDistributions(testDistribution); + }, [chipMap, maxDenomination, buyInAmount, playerCount]); return ( Chip Distribution Summary: - {/**Object.keys(chipCountPerPlayer).length > 0 ? ( - - {Object.entries(chipCountPerPlayer).map( - ([color, { count, value }]) => ( - - {color.charAt(0).toUpperCase() + color.slice(1)} chips: {count}{" "} - ( ${value} each) - - ) - )} + {colors.map((color, index) => ( + + + {String(color).charAt(0).toUpperCase() + String(color).slice(1)}{" "} + chips: {distributions[index]} ( ${denominations[index]} each) + - ) : ( - - No valid distribution calculated yet. - - )**/} + ))} ); }; -- 2.39.5 From dd75f585197bc297c1e99d260dadee3f51561411 Mon Sep 17 00:00:00 2001 From: David Westgate Date: Sun, 23 Feb 2025 23:20:36 -0800 Subject: [PATCH 5/9] more sophistication for invalid distriubtions --- components/ChipDistributionSummary.tsx | 132 ++++++++++++++++--------- 1 file changed, 83 insertions(+), 49 deletions(-) diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index 00858ba..e82dc28 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -75,28 +75,33 @@ const ChipDistributionSummary = ({ [totalChipsCount, playerCount] ); - function denominate(min: number, max: number, count: number = 4): number[] { - if (max - min + 1 < count) { - throw new Error( - "Range is too small to generate the required number of unique values." - ); + const redenominate = ( + invalidDenomination: validDenomination[], + shuffleIndex: number + ): validDenomination[] => { + console.log("Old Denominations ", invalidDenomination); + 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 { + const nextLowestDenominationIndex = Math.max( + validDenominations.indexOf(invalidDenomination[i]) - 1, + 0 + ); + newDenomination.push(validDenominations[nextLowestDenominationIndex]); + } } - - const numbers = new Set(); - - while (numbers.size < count) { - const randomNum = Math.floor(Math.random() * (max - min + 1)) + min; - numbers.add(randomNum); - } - - // const denominations - - return Array.from(numbers); - } + newDenomination.reverse(); + console.log("New Denominations ", newDenomination); + return newDenomination; + }; useEffect(() => { // const testDistribution: Map = new Map(); - const testDenomination: validDenomination[] = []; + let testDenomination: validDenomination[] = []; const numColors = chipMap.size; const testDistribution: number[] = []; @@ -118,44 +123,66 @@ const ChipDistributionSummary = ({ } testDenomination.reverse(); - console.log("BUY IN: ", buyInAmount); - console.log("PLAYER COUNT ", playerCount); + // console.log("BUY IN: ", buyInAmount); + // console.log("PLAYER COUNT ", playerCount); // DISTRIBUTE let remainingValue = buyInAmount; const remainingChips = [...maxPossibleDistribution]; - console.log("\ntest Denomination", testDenomination); - console.log("test distribution ", testDistribution); - console.log("remainingChips", remainingChips); - console.log("remaining value ", remainingValue); + // console.log("\ntest Denomination", testDenomination); + // console.log("test distribution ", testDistribution); + // console.log("remainingChips", remainingChips); + // console.log("remaining value ", remainingValue); //First distribute one of each chip to each player - for (let i = numColors - 1; i >= 0; i--) { - testDistribution[i] = testDistribution[i] + 1; - remainingChips[i] = remainingChips[i] - 1; - remainingValue = remainingValue - testDenomination[i]; - } - console.log("\ntest Denomination", testDenomination); - console.log("test distribution ", testDistribution); - console.log("remainingChips", remainingChips); - console.log("remaining value ", remainingValue); + // for (let i = numColors - 1; i >= 0; i--) { + // testDistribution[i] = testDistribution[i] + 1; + // remainingChips[i] = remainingChips[i] - 1; + // remainingValue = remainingValue - testDenomination[i]; + // } + // console.log("\ntest Denomination", testDenomination); + // console.log("test distribution ", testDistribution); + // console.log("remainingChips", remainingChips); + // console.log("remaining value ", remainingValue); //Then, greedy approach to distribute remaining chips - // while (remainingValue > 0) { - // for (let i = numColors - 1; i >= 0; i--) { - // if (remainingChips[i] > 0 && remainingValue > testDenomination[i]) { - // testDistribution[i] = testDistribution[i] + 1; - // remainingChips[i] = remainingChips[i] - 1; - // remainingValue = remainingValue - testDenomination[i]; - // } - // remainingValue = 0; - // } - // } - console.log("\ntest Denomination", testDenomination); - console.log("test distribution ", testDistribution); - console.log("remainingChips", remainingChips); - console.log("remaining value ", remainingValue); + let fail = true; + let failCount = 0; + while (fail && failCount < 10) { + let stop = false; + while (remainingValue > 0 && !stop) { + let distributed = false; + for (let i = numColors - 1; i >= 0; i--) { + if (remainingChips[i] > 0 && remainingValue > testDenomination[i]) { + // console.log("distributing ", testDenomination[i]); + testDistribution[i] = testDistribution[i] + 1; + remainingChips[i] = remainingChips[i] - 1; + remainingValue = remainingValue - testDenomination[i]; + distributed = true; + } + } + if (distributed == false) { + stop = true; + } + } + if (remainingValue !== 0) { + console.log(`\n Failed: ${remainingValue} !== 0 Redenominating `); + const redenominateIndex = numColors - (failCount % numColors); + console.log("Redenominating index ", redenominateIndex); + testDenomination = redenominate(testDenomination, redenominateIndex); + failCount += 1; + fail = true; + } else { + fail = false; + } + } + + // console.log("\ntest Denomination", testDenomination); + // console.log("test distribution ", testDistribution); + // console.log("remainingChips", remainingChips); + // console.log("remaining value ", remainingValue); + setDenominations(testDenomination); setDistributions(testDistribution); }, [chipMap, maxDenomination, buyInAmount, playerCount]); @@ -165,12 +192,17 @@ const ChipDistributionSummary = ({ Chip Distribution Summary: {colors.map((color, index) => ( - + {String(color).charAt(0).toUpperCase() + String(color).slice(1)}{" "} chips: {distributions[index]} ( ${denominations[index]} each) ))} + + Total Value:{" "} + {distributions?.length > 0 && + distributions.reduce((p, c, i) => p + c * denominations[i])} + ); }; @@ -189,10 +221,12 @@ const styles = StyleSheet.create({ }, chipContainer: { marginTop: 10, + backgroundColor: "#888", }, chipText: { - fontSize: 16, + fontSize: 20, marginVertical: 2, + fontWeight: "bold", }, noDataText: { fontSize: 16, -- 2.39.5 From 8d3641095b8a98ef85000fff6651402495a0a91c Mon Sep 17 00:00:00 2001 From: David Westgate Date: Mon, 24 Feb 2025 18:48:22 -0800 Subject: [PATCH 6/9] somewhat working algorithm --- components/ChipDistributionSummary.tsx | 72 +++++++++++++++++++++----- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index e82dc28..f0b9c63 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -64,11 +64,19 @@ const ChipDistributionSummary = ({ }, [chipMap]); // Total value of the pot - const totalValue = useMemo( + const potValue = useMemo( () => buyInAmount * playerCount, [buyInAmount, playerCount] ); + const totalValue = useMemo(() => { + let value = 0; + for (let i = 0; i < totalChipsCount.length; i++) { + value += distributions[i] * denominations[i]; + } + return value; + }, [distributions, denominations]); + // Maximum quantity of each chip which may be distributed to a single player before running out const maxPossibleDistribution = useMemo( () => totalChipsCount.map((v) => Math.floor(v / playerCount)), @@ -99,6 +107,34 @@ const ChipDistributionSummary = ({ return newDenomination; }; + const redenominate2 = ( + invalidDenomination: validDenomination[], + shuffleIndex: number + ): validDenomination[] => { + console.log("Old Denominations ", invalidDenomination); + 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(); + console.log("New Denominations ", newDenomination); + return newDenomination; + }; + useEffect(() => { // const testDistribution: Map = new Map(); let testDenomination: validDenomination[] = []; @@ -128,7 +164,6 @@ const ChipDistributionSummary = ({ // DISTRIBUTE let remainingValue = buyInAmount; - const remainingChips = [...maxPossibleDistribution]; // console.log("\ntest Denomination", testDenomination); // console.log("test distribution ", testDistribution); // console.log("remainingChips", remainingChips); @@ -153,13 +188,24 @@ const ChipDistributionSummary = ({ let stop = false; while (remainingValue > 0 && !stop) { let distributed = false; - for (let i = numColors - 1; i >= 0; i--) { - if (remainingChips[i] > 0 && remainingValue > testDenomination[i]) { - // console.log("distributing ", testDenomination[i]); - testDistribution[i] = testDistribution[i] + 1; - remainingChips[i] = remainingChips[i] - 1; - remainingValue = remainingValue - testDenomination[i]; - distributed = true; + for (let i = numColors - 1; i >= 0; i = i - 1) { + console.log("i: ", i); + if (testDistribution[i] < maxPossibleDistribution[i]) { + if (remainingValue >= testDenomination[i]) { + // console.log("distributing ", testDenomination[i]); + testDistribution[i] = testDistribution[i] + 1; + remainingValue = remainingValue - testDenomination[i]; + distributed = true; + } else { + // console.log( + // "Max distribution reached for value", + // testDenomination[i], + // "remaining value is ", + // remainingValue + // ); + } + } else { + // console.log("no more chips left for ", colors[i]); } } if (distributed == false) { @@ -168,9 +214,9 @@ const ChipDistributionSummary = ({ } if (remainingValue !== 0) { console.log(`\n Failed: ${remainingValue} !== 0 Redenominating `); - const redenominateIndex = numColors - (failCount % numColors); + const redenominateIndex = failCount % numColors; console.log("Redenominating index ", redenominateIndex); - testDenomination = redenominate(testDenomination, redenominateIndex); + testDenomination = redenominate2(testDenomination, redenominateIndex); failCount += 1; fail = true; } else { @@ -199,9 +245,7 @@ const ChipDistributionSummary = ({ ))} - Total Value:{" "} - {distributions?.length > 0 && - distributions.reduce((p, c, i) => p + c * denominations[i])} + Total Value:{totalValue} Pot Value: {potValue} ); -- 2.39.5 From 4cc8db3e9c1c4221dabbb6f6e668a11648116baf Mon Sep 17 00:00:00 2001 From: David Westgate Date: Mon, 24 Feb 2025 19:44:27 -0800 Subject: [PATCH 7/9] improve styles --- components/ChipDistributionSummary.tsx | 140 ++++++++++--------------- 1 file changed, 54 insertions(+), 86 deletions(-) diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index f0b9c63..4138509 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useMemo, useState } from "react"; import { View, Text, StyleSheet } from "react-native"; import { ColorValue } from "react-native"; -// import { COLORS } from "@/app"; interface ChipDistributionSummaryProps { playerCount: number; @@ -45,7 +44,7 @@ const ChipDistributionSummary = ({ return m; }, [playerCount, buyInAmount, totalChipsCount]); - // Helper function to return the closest (but lower) valid denomination to the target + // Return the closest (but lower) valid denomination to the target const findFloorDenomination = (target: number): validDenomination => { let current: validDenomination = validDenominations[0]; validDenominations.forEach((value, index) => { @@ -55,6 +54,7 @@ const ChipDistributionSummary = ({ }; // Bound for the value of the highest chip + // This is somewhat arbitray, but 1/3 to 1/4 is reasonable depending on the number of colors. const maxDenomination = useMemo(() => { if (chipMap.size > 3) { return findFloorDenomination(buyInAmount / 3); @@ -69,6 +69,7 @@ const ChipDistributionSummary = ({ [buyInAmount, playerCount] ); + // The total value of all chips distributed to a single player. Ideally should be equal to buyInAmount const totalValue = useMemo(() => { let value = 0; for (let i = 0; i < totalChipsCount.length; i++) { @@ -77,41 +78,18 @@ const ChipDistributionSummary = ({ return value; }, [distributions, denominations]); - // Maximum quantity of each chip which may be distributed to a single player before running out + // Maximum quantity of each chip color which may be distributed to a single player before running out const maxPossibleDistribution = useMemo( () => totalChipsCount.map((v) => Math.floor(v / playerCount)), [totalChipsCount, playerCount] ); + // Redenominate the chips in case of failure to properly distribute. + // Move the shuffle index to the next lowest denomination, and keep all else same const redenominate = ( invalidDenomination: validDenomination[], shuffleIndex: number ): validDenomination[] => { - console.log("Old Denominations ", invalidDenomination); - 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 { - const nextLowestDenominationIndex = Math.max( - validDenominations.indexOf(invalidDenomination[i]) - 1, - 0 - ); - newDenomination.push(validDenominations[nextLowestDenominationIndex]); - } - } - newDenomination.reverse(); - console.log("New Denominations ", newDenomination); - return newDenomination; - }; - - const redenominate2 = ( - invalidDenomination: validDenomination[], - shuffleIndex: number - ): validDenomination[] => { - console.log("Old Denominations ", invalidDenomination); let moved = false; const newDenomination: validDenomination[] = []; for (let i = invalidDenomination.length - 1; i >= 0; i--) { @@ -131,12 +109,10 @@ const ChipDistributionSummary = ({ } } newDenomination.reverse(); - console.log("New Denominations ", newDenomination); return newDenomination; }; useEffect(() => { - // const testDistribution: Map = new Map(); let testDenomination: validDenomination[] = []; const numColors = chipMap.size; @@ -146,7 +122,6 @@ const ChipDistributionSummary = ({ } let numColorsRemaining = numColors; - // testDistribution.set(colors[numColors - 1], maxDenomination); testDenomination.push(maxDenomination); numColorsRemaining -= 1; let currentDenominationIndex: number = @@ -159,53 +134,21 @@ const ChipDistributionSummary = ({ } testDenomination.reverse(); - // console.log("BUY IN: ", buyInAmount); - // console.log("PLAYER COUNT ", playerCount); - - // DISTRIBUTE let remainingValue = buyInAmount; - // console.log("\ntest Denomination", testDenomination); - // console.log("test distribution ", testDistribution); - // console.log("remainingChips", remainingChips); - // console.log("remaining value ", remainingValue); - - //First distribute one of each chip to each player - // for (let i = numColors - 1; i >= 0; i--) { - // testDistribution[i] = testDistribution[i] + 1; - // remainingChips[i] = remainingChips[i] - 1; - // remainingValue = remainingValue - testDenomination[i]; - // } - // console.log("\ntest Denomination", testDenomination); - // console.log("test distribution ", testDistribution); - // console.log("remainingChips", remainingChips); - // console.log("remaining value ", remainingValue); - - //Then, greedy approach to distribute remaining chips let fail = true; let failCount = 0; - while (fail && failCount < 10) { + while (fail && failCount < 1) { let stop = false; while (remainingValue > 0 && !stop) { let distributed = false; for (let i = numColors - 1; i >= 0; i = i - 1) { - console.log("i: ", i); if (testDistribution[i] < maxPossibleDistribution[i]) { if (remainingValue >= testDenomination[i]) { - // console.log("distributing ", testDenomination[i]); testDistribution[i] = testDistribution[i] + 1; remainingValue = remainingValue - testDenomination[i]; distributed = true; - } else { - // console.log( - // "Max distribution reached for value", - // testDenomination[i], - // "remaining value is ", - // remainingValue - // ); } - } else { - // console.log("no more chips left for ", colors[i]); } } if (distributed == false) { @@ -213,10 +156,8 @@ const ChipDistributionSummary = ({ } } if (remainingValue !== 0) { - console.log(`\n Failed: ${remainingValue} !== 0 Redenominating `); const redenominateIndex = failCount % numColors; - console.log("Redenominating index ", redenominateIndex); - testDenomination = redenominate2(testDenomination, redenominateIndex); + testDenomination = redenominate(testDenomination, redenominateIndex); failCount += 1; fail = true; } else { @@ -224,29 +165,39 @@ const ChipDistributionSummary = ({ } } - // console.log("\ntest Denomination", testDenomination); - // console.log("test distribution ", testDistribution); - // console.log("remainingChips", remainingChips); - // console.log("remaining value ", remainingValue); - setDenominations(testDenomination); setDistributions(testDistribution); }, [chipMap, maxDenomination, buyInAmount, playerCount]); return ( - Chip Distribution Summary: - {colors.map((color, index) => ( - - - {String(color).charAt(0).toUpperCase() + String(color).slice(1)}{" "} - chips: {distributions[index]} ( ${denominations[index]} each) - - - ))} - - Total Value:{totalValue} Pot Value: {potValue} - + Distribution & Denomination + ${potValue} Pot + + {totalChipsCount.map((_, index) => ( + + + {distributions[index]} chips: + + + ${denominations[index]} each + + + ))} + + Total Value:{totalValue} ); }; @@ -255,23 +206,40 @@ const styles = StyleSheet.create({ container: { marginTop: 20, padding: 15, - backgroundColor: "#F8F9FA", borderRadius: 10, + display: "flex", + alignItems: "center", }, title: { fontSize: 18, fontWeight: "bold", marginBottom: 10, }, + subTitle: { + fontSize: 16, + color: "gray", + fontWeight: "bold", + marginBottom: 10, + }, chipContainer: { marginTop: 10, - backgroundColor: "#888", + }, + chipRow: { + display: "flex", + flexDirection: "row", + justifyContent: "center", + gap: 10, }, chipText: { fontSize: 20, marginVertical: 2, fontWeight: "bold", }, + whiteShadow: { + textShadowColor: "black", + textShadowOffset: { width: 0, height: 0 }, + textShadowRadius: 10, + }, noDataText: { fontSize: 16, color: "gray", -- 2.39.5 From 0f5d7316de37f2f4469f981be90f121d845b658b Mon Sep 17 00:00:00 2001 From: David Westgate Date: Mon, 24 Feb 2025 22:02:17 -0800 Subject: [PATCH 8/9] optimizations and comments --- components/ChipDistributionSummary.tsx | 81 ++++++++++++-------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/components/ChipDistributionSummary.tsx b/components/ChipDistributionSummary.tsx index 4138509..c111dd4 100644 --- a/components/ChipDistributionSummary.tsx +++ b/components/ChipDistributionSummary.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { View, Text, StyleSheet } from "react-native"; import { ColorValue } from "react-native"; @@ -35,15 +35,6 @@ const ChipDistributionSummary = ({ | 50 | 100; - // Re-organize the inputs in a map for convience - const chipMap: Map = useMemo(() => { - const m: Map = new Map(); - totalChipsCount.map((v, i) => { - m.set(colors[i], v); - }); - return m; - }, [playerCount, buyInAmount, totalChipsCount]); - // Return the closest (but lower) valid denomination to the target const findFloorDenomination = (target: number): validDenomination => { let current: validDenomination = validDenominations[0]; @@ -56,12 +47,12 @@ const ChipDistributionSummary = ({ // Bound for the value of the highest chip // This is somewhat arbitray, but 1/3 to 1/4 is reasonable depending on the number of colors. const maxDenomination = useMemo(() => { - if (chipMap.size > 3) { + if (totalChipsCount.length > 3) { return findFloorDenomination(buyInAmount / 3); } else { return findFloorDenomination(buyInAmount / 4); } - }, [chipMap]); + }, [totalChipsCount]); // Total value of the pot const potValue = useMemo( @@ -86,56 +77,60 @@ const ChipDistributionSummary = ({ // Redenominate the chips in case of failure to properly distribute. // Move the shuffle index to the next lowest denomination, and keep all else same - const redenominate = ( - 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]); + const redenominate = useCallback( + ( + 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; - }; + newDenomination.reverse(); + return newDenomination; + }, + [] + ); + // Dynamically set denominations and distributions from changing inputs useEffect(() => { let testDenomination: validDenomination[] = []; - const numColors = chipMap.size; + const numColors = totalChipsCount.length; const testDistribution: number[] = []; for (let i = 0; i < numColors; ++i) { testDistribution.push(0); } - let numColorsRemaining = numColors; + // Start with max denominations, then push on the next adjacent lower denomination testDenomination.push(maxDenomination); - numColorsRemaining -= 1; let currentDenominationIndex: number = validDenominations.indexOf(maxDenomination); - while (numColorsRemaining > 0) { - numColorsRemaining -= 1; + for (let i = numColors - 2; i >= 0; i = i - 1) { currentDenominationIndex -= 1; const currentDemoniation = validDenominations[currentDenominationIndex]; testDenomination.push(currentDemoniation); } testDenomination.reverse(); + // Distribute the chips using the test denomination + // If distribution fails to equal the buy-in, redenominate and re-try + // Algorithm could be improved with more complexity and optimization let remainingValue = buyInAmount; - let fail = true; let failCount = 0; while (fail && failCount < 1) { @@ -167,7 +162,7 @@ const ChipDistributionSummary = ({ setDenominations(testDenomination); setDistributions(testDistribution); - }, [chipMap, maxDenomination, buyInAmount, playerCount]); + }, [totalChipsCount, maxDenomination, buyInAmount, playerCount]); return ( -- 2.39.5 From 85c4a9d518322891f395dcbad9b2109422493c11 Mon Sep 17 00:00:00 2001 From: David Westgate Date: Mon, 24 Feb 2025 22:21:28 -0800 Subject: [PATCH 9/9] fix tests --- components/__tests__/BuyInSelector.test.tsx | 6 +-- .../ChipDistributionSummary.test.tsx | 41 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/components/__tests__/BuyInSelector.test.tsx b/components/__tests__/BuyInSelector.test.tsx index 6f6706f..c7601be 100644 --- a/components/__tests__/BuyInSelector.test.tsx +++ b/components/__tests__/BuyInSelector.test.tsx @@ -54,7 +54,7 @@ describe("BuyInSelector Component", () => { fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "-10"); - expect(setBuyInAmount).toHaveBeenCalledWith(null); + expect(setBuyInAmount).toHaveBeenCalledWith(25); }); it("clears the custom amount when selecting a predefined option", () => { @@ -80,9 +80,9 @@ describe("BuyInSelector Component", () => { expect(setBuyInAmount).toHaveBeenCalledWith(75); fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "-5"); - expect(setBuyInAmount).toHaveBeenCalledWith(null); + expect(setBuyInAmount).toHaveBeenCalledWith(25); fireEvent.changeText(getByPlaceholderText("Enter custom buy-in"), "abc"); - expect(setBuyInAmount).toHaveBeenCalledWith(null); + expect(setBuyInAmount).toHaveBeenCalledWith(25); }); }); diff --git a/components/__tests__/ChipDistributionSummary.test.tsx b/components/__tests__/ChipDistributionSummary.test.tsx index 2646cca..c864aa2 100644 --- a/components/__tests__/ChipDistributionSummary.test.tsx +++ b/components/__tests__/ChipDistributionSummary.test.tsx @@ -5,35 +5,45 @@ import ChipDistributionSummary from "../ChipDistributionSummary"; describe("ChipDistributionSummary Component", () => { test("renders correctly with valid data", () => { const playerCount = 4; - const totalChipsCount = [100, 200, 300, 400, 500]; + const totalChipsCount = [100, 80, 60, 40, 20]; const colors = ["WHITE", "RED", "GREEN", "BLUE", "BLACK"]; + const buyInAmount = 20; - // Update this to match the actual component's chip distribution logic - const expectedDistribution = [8, 16, 25, 33, 41]; // Adjust based on actual component calculations + const expectedDistribution = [2, 2, 1, 2, 2]; + const expectedDenominations = [0.5, 1, 2, 2.5, 5]; - const { getByText } = render( + const { getByText, getAllByText } = render( ); - expect(getByText("Chip Distribution Summary:")).toBeTruthy(); + expect(getByText("Distribution & Denomination")).toBeTruthy(); expectedDistribution.forEach((count, index) => { - expect(getByText(new RegExp(`${colors[index]} Chips: ${count} per player`, "i"))).toBeTruthy(); + expect(getAllByText(new RegExp(`${count} chips:`, "i"))).toBeTruthy(); + expect( + getByText(new RegExp(`\\$${expectedDenominations[index]} each`, "i")) + ).toBeTruthy(); }); }); - test("renders fallback message when no valid distribution", () => { + // Case not currently supported + test.skip("renders fallback message when no valid distribution", () => { const { getByText } = render( - + ); expect(getByText("No valid distribution calculated yet.")).toBeTruthy(); }); - test("scales down chips if exceeding MAX_CHIPS", () => { + // Case not currently supported + test.skip("scales down chips if exceeding MAX_CHIPS", () => { const playerCount = 2; let totalChipsCount = [300, 400, 500, 600, 700]; const MAX_CHIPS = 500; @@ -41,11 +51,12 @@ describe("ChipDistributionSummary Component", () => { if (totalChips > MAX_CHIPS) { const scaleFactor = MAX_CHIPS / totalChips; - totalChipsCount = totalChipsCount.map(count => Math.round(count * scaleFactor)); + totalChipsCount = totalChipsCount.map((count) => + Math.round(count * scaleFactor) + ); } const expectedDistribution = [30, 40, 50, 60, 70]; // Adjust to match actual component calculations - const colors = ["WHITE", "RED", "GREEN", "BLUE", "BLACK"]; const { getByText } = render( { /> ); - expect(getByText("Chip Distribution Summary:")).toBeTruthy(); + expect(getByText("Distribution & Denomination")).toBeTruthy(); expectedDistribution.forEach((count, index) => { - expect(getByText(new RegExp(`${colors[index]} Chips: ${count} per player`, "i"))).toBeTruthy(); + expect(getByText(new RegExp(`${count} chips:`, "i"))).toBeTruthy(); + // expect(getByText(new RegExp(`$${count} each`, "i"))).toBeTruthy(); }); }); }); - -- 2.39.5