Skip to main content
Glama
bsim0927
by bsim0927
validator.js6.15 kB
/** * Parameter validation for solver */ import { isValidBoard, isValidCard, POSITIONS, POST_FLOP_STREETS, ACTIONS } from '../utils/constants.js'; export class SolverValidator { /** * Validate pot size * @param {number} pot * @throws {Error} */ static validatePot(pot) { if (!Number.isFinite(pot) || pot <= 0) { throw new Error('Pot must be a positive number'); } } /** * Validate effective stack * @param {number} stack * @throws {Error} */ static validateStack(stack) { if (!Number.isFinite(stack) || stack <= 0) { throw new Error('Effective stack must be a positive number'); } } /** * Validate pot and stack relationship * @param {number} pot * @param {number} stack * @throws {Error} */ static validatePotStackRelationship(pot, stack) { if (pot > stack * 2) { throw new Error('Pot cannot be more than 2x the effective stack'); } } /** * Validate board cards * @param {string} board - Board string (e.g., "As,Kh,Qd") * @throws {Error} */ static validateBoard(board) { if (board && !isValidBoard(board)) { throw new Error(`Invalid board format: "${board}". Use format like "As,Kh,Qd"`); } } /** * Validate hand range format * @param {string} range - Range string (e.g., "AA-22,AK-AJ") * @throws {Error} */ static validateRange(range) { if (!range || typeof range !== 'string' || range.trim() === '') { throw new Error('Range must be a non-empty string'); } // Basic validation - just check that it has some hand notation const hasHandNotation = /[A-Z]/.test(range); if (!hasHandNotation) { throw new Error(`Invalid range format: "${range}". Expected poker hand notation like "AA", "AKs", "22-99"`); } } /** * Validate bet size array * @param {Array<number>} sizes - Array of bet sizes as percentages * @throws {Error} */ static validateBetSizes(sizes) { if (!Array.isArray(sizes)) { throw new Error('Bet sizes must be an array'); } for (const size of sizes) { if (!Number.isFinite(size) || size <= 0 || size > 200) { throw new Error(`Invalid bet size: ${size}. Must be a number between 0 and 200`); } } } /** * Validate bet sizes object structure * @param {Object} betSizes - Bet sizes configuration object * @throws {Error} */ static validateBetSizesObject(betSizes) { if (!betSizes || typeof betSizes !== 'object') { return; // Optional parameter } for (const position of Object.keys(betSizes)) { if (!['ip', 'oop'].includes(position)) { throw new Error(`Invalid position: ${position}. Must be "ip" or "oop"`); } const positionBets = betSizes[position]; if (!positionBets || typeof positionBets !== 'object') { throw new Error(`Position ${position} must have bet size configuration`); } for (const street of POST_FLOP_STREETS) { if (positionBets[street]) { const streetBets = positionBets[street]; if (typeof streetBets !== 'object') { throw new Error(`Invalid bet sizes for ${position} ${street}`); } for (const action of Object.keys(streetBets)) { if (!['bet', 'raise', 'donk', 'allin', 'check'].includes(action)) { throw new Error(`Invalid action: ${action}`); } const sizeValue = streetBets[action]; if (sizeValue === 'allin') { continue; // Valid } if (Array.isArray(sizeValue)) { this.validateBetSizes(sizeValue); } } } } } } /** * Validate solver configuration * @param {Object} config - Solver config * @throws {Error} */ static validateSolverConfig(config) { if (!config || typeof config !== 'object') { return; // Optional parameter } if ('thread_num' in config) { if (!Number.isInteger(config.thread_num) || config.thread_num < 1 || config.thread_num > 64) { throw new Error('thread_num must be an integer between 1 and 64'); } } if ('accuracy' in config) { if (!Number.isFinite(config.accuracy) || config.accuracy < 0.1 || config.accuracy > 10) { throw new Error('accuracy must be a number between 0.1 and 10'); } } if ('max_iteration' in config) { if (!Number.isInteger(config.max_iteration) || config.max_iteration < 1) { throw new Error('max_iteration must be a positive integer'); } } if ('print_interval' in config) { if (!Number.isInteger(config.print_interval) || config.print_interval < 1) { throw new Error('print_interval must be a positive integer'); } } if ('use_isomorphism' in config) { if (typeof config.use_isomorphism !== 'boolean' && ![0, 1].includes(config.use_isomorphism)) { throw new Error('use_isomorphism must be a boolean or 0/1'); } } if ('allin_threshold' in config) { if (!Number.isFinite(config.allin_threshold) || config.allin_threshold < 0 || config.allin_threshold > 1) { throw new Error('allin_threshold must be a number between 0 and 1'); } } if ('dump_rounds' in config) { if (!Number.isInteger(config.dump_rounds) || ![0, 1, 2].includes(config.dump_rounds)) { throw new Error('dump_rounds must be 0 (flop), 1 (turn), or 2 (river)'); } } } /** * Validate all solver parameters * @param {Object} params * @throws {Error} */ static validateAll(params) { const { pot, effective_stack, board, range_ip, range_oop, bet_sizes, solver_config } = params; // Required parameters this.validatePot(pot); this.validateStack(effective_stack); this.validatePotStackRelationship(pot, effective_stack); this.validateBoard(board); this.validateRange(range_ip); this.validateRange(range_oop); // Optional parameters this.validateBetSizesObject(bet_sizes); this.validateSolverConfig(solver_config); return true; } } export default SolverValidator;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bsim0927/texas-solver-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server